Merge "Call removeOverlayView() before onRelease()" into lmp-dev
diff --git a/Android.mk b/Android.mk
index 4321f8b..278e67f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -155,6 +155,7 @@
core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl \
core/java/android/hardware/hdmi/IHdmiHotplugEventListener.aidl \
core/java/android/hardware/hdmi/IHdmiInputChangeListener.aidl \
+ core/java/android/hardware/hdmi/IHdmiRecordRequestListener.aidl \
core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl \
core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl \
core/java/android/hardware/input/IInputManager.aidl \
@@ -296,6 +297,7 @@
location/java/android/location/IGeocodeProvider.aidl \
location/java/android/location/IGeofenceProvider.aidl \
location/java/android/location/IGpsMeasurementsListener.aidl \
+ location/java/android/location/IGpsNavigationMessageListener.aidl \
location/java/android/location/IGpsStatusListener.aidl \
location/java/android/location/IGpsStatusProvider.aidl \
location/java/android/location/ILocationListener.aidl \
@@ -321,15 +323,17 @@
media/java/android/media/IRemoteVolumeObserver.aidl \
media/java/android/media/IRingtonePlayer.aidl \
media/java/android/media/IVolumeController.aidl \
+ media/java/android/media/browse/IMediaBrowserService.aidl \
+ media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl \
+ media/java/android/media/projection/IMediaProjection.aidl \
+ media/java/android/media/projection/IMediaProjectionCallback.aidl \
+ media/java/android/media/projection/IMediaProjectionManager.aidl \
media/java/android/media/routing/IMediaRouteService.aidl \
media/java/android/media/routing/IMediaRouteClientCallback.aidl \
media/java/android/media/routing/IMediaRouter.aidl \
media/java/android/media/routing/IMediaRouterDelegate.aidl \
media/java/android/media/routing/IMediaRouterRoutingCallback.aidl \
media/java/android/media/routing/IMediaRouterStateCallback.aidl \
- media/java/android/media/projection/IMediaProjection.aidl \
- media/java/android/media/projection/IMediaProjectionCallback.aidl \
- media/java/android/media/projection/IMediaProjectionManager.aidl \
media/java/android/media/session/IActiveSessionsListener.aidl \
media/java/android/media/session/ISessionController.aidl \
media/java/android/media/session/ISessionControllerCallback.aidl \
@@ -390,72 +394,27 @@
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVA_LIBRARIES := core-libart conscrypt okhttp core-junit bouncycastle ext
-LOCAL_MODULE := framework-base
+LOCAL_MODULE := framework
+
+LOCAL_DX_FLAGS := --core-library --multi-dex
LOCAL_RMTYPEDEFS := true
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Make sure that R.java and Manifest.java are built before we build
-# the source for this library.
-framework_res_R_stamp := \
- $(call intermediates-dir-for,APPS,framework-res,,COMMON)/src/R.stamp
-$(full_classes_compiled_jar): $(framework_res_R_stamp)
-
-# Build part 1 of the framework library.
-# ============================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := framework
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_STATIC_JAVA_LIBRARIES := framework-base
-LOCAL_DX_FLAGS := --core-library
-
-# List of packages to include along with their descendants.
-LOCAL_JAR_PACKAGES := \
- android
-
-# List of packages to exclude along with their descendants.
-# Overrides inclusion.
-LOCAL_JAR_EXCLUDE_PACKAGES := \
- android.filterfw \
- android.filterpacks \
- android.hardware
-
# List of classes and interfaces which should be loaded by the Zygote.
LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/preloaded-classes
include $(BUILD_JAVA_LIBRARY)
framework_module := $(LOCAL_INSTALLED_MODULE)
-# Build part 2 of the framework library.
-# ============================================================
-include $(CLEAR_VARS)
+# Make sure that R.java and Manifest.java are built before we build
+# the source for this library.
+framework_res_R_stamp := \
+ $(call intermediates-dir-for,APPS,framework-res,,COMMON)/src/R.stamp
+$(full_classes_compiled_jar): $(framework_res_R_stamp)
-LOCAL_MODULE := framework2
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_STATIC_JAVA_LIBRARIES := framework-base
-LOCAL_DX_FLAGS := --core-library
-
-# List of packages to include along with their descendants.
-LOCAL_JAR_PACKAGES := \
- android.filterfw \
- android.filterpacks \
- android.hardware \
- com \
- javax
-
-include $(BUILD_JAVA_LIBRARY)
-framework2_module := $(LOCAL_INSTALLED_MODULE)
-
-# Make sure that all framework modules are installed when framework is.
-# ============================================================
$(framework_module): | $(dir $(framework_module))framework-res.apk
-$(framework_module): | $(dir $(framework_module))framework2.jar
-framework_built := $(call java-lib-deps,framework framework2)
+framework_built := $(call java-lib-deps,framework)
# Copy AIDL files to be preprocessed and included in the SDK,
# specified relative to the root of the build tree.
@@ -490,6 +449,7 @@
frameworks/base/core/java/android/os/DropBoxManager.aidl \
frameworks/base/core/java/android/os/ParcelFileDescriptor.aidl \
frameworks/base/core/java/android/os/ParcelUuid.aidl \
+ frameworks/base/core/java/android/os/PersistableBundle.aidl \
frameworks/base/core/java/android/print/PrinterInfo.aidl \
frameworks/base/core/java/android/print/PageRange.aidl \
frameworks/base/core/java/android/print/PrintAttributes.aidl \
@@ -631,7 +591,6 @@
okhttp \
ext \
framework \
- framework2 \
telephony-common \
voip-common
@@ -673,7 +632,7 @@
-overview $(LOCAL_PATH)/core/java/overview.html
framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
- $(call intermediates-dir-for,JAVA_LIBRARIES,framework-base,,COMMON)
+ $(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \
$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR) \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 2c9ea54..3014e0f 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -210,6 +210,8 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/ims-common_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework2_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/telecomm/java/com/android/internal/telecomm)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework.* $(PRODUCT_OUT)/system/framework2.*)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/api/current.txt b/api/current.txt
index 6dc2bb1..0361c7d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -380,6 +380,8 @@
field public static final int centerY = 16843171; // 0x10101a3
field public static final int checkBoxPreferenceStyle = 16842895; // 0x101008f
field public static final int checkMark = 16843016; // 0x1010108
+ field public static final int checkMarkTint = 16843949; // 0x10104ad
+ field public static final int checkMarkTintMode = 16843950; // 0x10104ae
field public static final int checkable = 16843237; // 0x10101e5
field public static final int checkableBehavior = 16843232; // 0x10101e0
field public static final int checkboxStyle = 16842860; // 0x101006c
@@ -744,6 +746,7 @@
field public static final int l_resource_pad23 = 16843770; // 0x10103fa
field public static final int l_resource_pad24 = 16843769; // 0x10103f9
field public static final int l_resource_pad25 = 16843768; // 0x10103f8
+ field public static final int l_resource_pad26 = 16843767; // 0x10103f7
field public static final int l_resource_pad3 = 16843790; // 0x101040e
field public static final int l_resource_pad4 = 16843789; // 0x101040d
field public static final int l_resource_pad5 = 16843788; // 0x101040c
@@ -945,6 +948,7 @@
field public static final int popupKeyboard = 16843331; // 0x1010243
field public static final int popupLayout = 16843323; // 0x101023b
field public static final int popupMenuStyle = 16843520; // 0x1010300
+ field public static final int popupTheme = 16843951; // 0x10104af
field public static final int popupWindowStyle = 16842870; // 0x1010076
field public static final int port = 16842793; // 0x1010029
field public static final int positiveButtonText = 16843253; // 0x10101f5
@@ -1060,10 +1064,6 @@
field public static final int scrollingCache = 16843006; // 0x10100fe
field public static final deprecated int searchButtonText = 16843269; // 0x1010205
field public static final int searchIcon = 16843909; // 0x1010485
- field public static final int searchKeyphrase = 16843873; // 0x1010461
- field public static final int searchKeyphraseId = 16843872; // 0x1010460
- field public static final int searchKeyphraseRecognitionFlags = 16843948; // 0x10104ac
- field public static final int searchKeyphraseSupportedLocales = 16843874; // 0x1010462
field public static final int searchMode = 16843221; // 0x10101d5
field public static final int searchSettingsDescription = 16843402; // 0x101028a
field public static final int searchSuggestAuthority = 16843222; // 0x10101d6
@@ -1304,6 +1304,7 @@
field public static final int toXScale = 16843203; // 0x10101c3
field public static final int toYDelta = 16843209; // 0x10101c9
field public static final int toYScale = 16843205; // 0x10101c5
+ field public static final int toolbarStyle = 16843952; // 0x10104b0
field public static final int top = 16843182; // 0x10101ae
field public static final int topBright = 16842955; // 0x10100cb
field public static final int topDark = 16842951; // 0x10100c7
@@ -1327,7 +1328,6 @@
field public static final int trimPathEnd = 16843813; // 0x1010425
field public static final int trimPathOffset = 16843814; // 0x1010426
field public static final int trimPathStart = 16843812; // 0x1010424
- field public static final int tvInputType = 16843767; // 0x10103f7
field public static final int type = 16843169; // 0x10101a1
field public static final int typeface = 16842902; // 0x1010096
field public static final int uiOptions = 16843672; // 0x1010398
@@ -2742,6 +2742,7 @@
method public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthTokenByFeatures(java.lang.String, java.lang.String, java.lang.String[], android.app.Activity, android.os.Bundle, android.os.Bundle, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AuthenticatorDescription[] getAuthenticatorTypes();
method public java.lang.String getPassword(android.accounts.Account);
+ method public java.lang.String getPreviousName(android.accounts.Account);
method public java.lang.String getUserData(android.accounts.Account, java.lang.String);
method public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, java.lang.String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
method public void invalidateAuthToken(java.lang.String, java.lang.String);
@@ -2749,6 +2750,7 @@
method public java.lang.String peekAuthToken(android.accounts.Account, java.lang.String);
method public android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);
+ method public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler);
method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
method public void setPassword(android.accounts.Account, java.lang.String);
method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String);
@@ -3818,6 +3820,7 @@
method public void startWatchingMode(java.lang.String, java.lang.String, android.app.AppOpsManager.OnOpChangedListener);
method public void stopWatchingMode(android.app.AppOpsManager.OnOpChangedListener);
field public static final int MODE_ALLOWED = 0; // 0x0
+ field public static final int MODE_DEFAULT = 3; // 0x3
field public static final int MODE_ERRORED = 2; // 0x2
field public static final int MODE_IGNORED = 1; // 0x1
field public static final java.lang.String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -4581,6 +4584,7 @@
field public static final int DEFAULT_SOUND = 1; // 0x1
field public static final int DEFAULT_VIBRATE = 2; // 0x2
field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+ field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
@@ -5347,6 +5351,7 @@
method public int getPasswordMinimumSymbols(android.content.ComponentName);
method public int getPasswordMinimumUpperCase(android.content.ComponentName);
method public int getPasswordQuality(android.content.ComponentName);
+ method public boolean getScreenCaptureDisabled(android.content.ComponentName);
method public boolean getStorageEncryption(android.content.ComponentName);
method public int getStorageEncryptionStatus();
method public boolean hasAnyCaCertsInstalled();
@@ -5355,7 +5360,7 @@
method public boolean installCaCert(android.content.ComponentName, byte[]);
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
- method public boolean isApplicationBlocked(android.content.ComponentName, java.lang.String);
+ method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(java.lang.String);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -5365,9 +5370,9 @@
method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
method public boolean resetPassword(java.lang.String, int);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
- method public boolean setApplicationBlocked(android.content.ComponentName, java.lang.String, boolean);
+ method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
- method public int setApplicationsBlocked(android.content.ComponentName, android.content.Intent, boolean);
+ method public int setApplicationsHidden(android.content.ComponentName, android.content.Intent, boolean);
method public void setBlockUninstall(android.content.ComponentName, java.lang.String, boolean);
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
@@ -5391,6 +5396,7 @@
method public void setProfileName(android.content.ComponentName, java.lang.String);
method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
+ method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
@@ -6660,7 +6666,7 @@
public abstract class AbstractRestrictionsProvider extends android.content.BroadcastReceiver {
ctor public AbstractRestrictionsProvider();
method public void onReceive(android.content.Context, android.content.Intent);
- method public abstract void requestPermission(android.content.Context, java.lang.String, java.lang.String, android.os.Bundle);
+ method public abstract void requestPermission(android.content.Context, java.lang.String, java.lang.String, java.lang.String, android.os.PersistableBundle);
}
public abstract class AbstractThreadedSyncAdapter {
@@ -8050,14 +8056,17 @@
public class RestrictionsManager {
method public android.os.Bundle getApplicationRestrictions();
+ method public android.content.Intent getLocalApprovalIntent();
method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String);
method public boolean hasRestrictionsProvider();
- method public void notifyPermissionResponse(java.lang.String, android.os.Bundle);
- method public void requestPermission(java.lang.String, android.os.Bundle);
- field public static final java.lang.String ACTION_PERMISSION_RESPONSE_RECEIVED = "android.intent.action.PERMISSION_RESPONSE_RECEIVED";
+ method public void notifyPermissionResponse(java.lang.String, android.os.PersistableBundle);
+ method public void requestPermission(java.lang.String, java.lang.String, android.os.PersistableBundle);
+ field public static final java.lang.String ACTION_PERMISSION_RESPONSE_RECEIVED = "android.content.action.PERMISSION_RESPONSE_RECEIVED";
+ field public static final java.lang.String ACTION_REQUEST_LOCAL_APPROVAL = "android.content.action.REQUEST_LOCAL_APPROVAL";
field public static final java.lang.String ACTION_REQUEST_PERMISSION = "android.content.action.REQUEST_PERMISSION";
field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
field public static final java.lang.String EXTRA_REQUEST_BUNDLE = "android.content.extra.REQUEST_BUNDLE";
+ field public static final java.lang.String EXTRA_REQUEST_ID = "android.content.extra.REQUEST_ID";
field public static final java.lang.String EXTRA_REQUEST_TYPE = "android.content.extra.REQUEST_TYPE";
field public static final java.lang.String EXTRA_RESPONSE_BUNDLE = "android.content.extra.RESPONSE_BUNDLE";
field public static final java.lang.String META_DATA_APP_RESTRICTIONS = "android.content.APP_RESTRICTIONS";
@@ -8070,7 +8079,6 @@
field public static final java.lang.String REQUEST_KEY_NEW_REQUEST = "android.request.new_request";
field public static final java.lang.String REQUEST_KEY_TITLE = "android.request.title";
field public static final java.lang.String REQUEST_TYPE_APPROVAL = "android.request.type.approval";
- field public static final java.lang.String REQUEST_TYPE_LOCAL_APPROVAL = "android.request.type.local_approval";
field public static final java.lang.String RESPONSE_KEY_ERROR_CODE = "android.response.errorcode";
field public static final java.lang.String RESPONSE_KEY_MESSAGE = "android.response.msg";
field public static final java.lang.String RESPONSE_KEY_RESPONSE_TIMESTAMP = "android.response.timestamp";
@@ -8278,7 +8286,6 @@
field public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1; // 0x1
field public static final int DOCUMENT_LAUNCH_NEVER = 3; // 0x3
field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0
- field public static final int DO_NOT_PERSIST = 1; // 0x1
field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40
field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8
field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000
@@ -8298,6 +8305,7 @@
field public static final int LAUNCH_SINGLE_TASK = 2; // 0x2
field public static final int LAUNCH_SINGLE_TOP = 1; // 0x1
field public static final int PERSIST_ACROSS_REBOOTS = 2; // 0x2
+ field public static final int PERSIST_NEVER = 1; // 0x1
field public static final int PERSIST_ROOT_ONLY = 0; // 0x0
field public static final int SCREEN_ORIENTATION_BEHIND = 3; // 0x3
field public static final int SCREEN_ORIENTATION_FULL_SENSOR = 10; // 0xa
@@ -8759,6 +8767,7 @@
field public static final java.lang.String FEATURE_MICROPHONE = "android.hardware.microphone";
field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
+ field public static final java.lang.String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
field public static final java.lang.String FEATURE_PRINTING = "android.software.print";
field public static final java.lang.String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
field public static final java.lang.String FEATURE_SCREEN_PORTRAIT = "android.hardware.screen.portrait";
@@ -8872,6 +8881,7 @@
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int FLAG_COSTS_MONEY = 1; // 0x1
field public static final int PROTECTION_DANGEROUS = 1; // 0x1
+ field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40
field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20
field public static final int PROTECTION_FLAG_SYSTEM = 16; // 0x10
field public static final int PROTECTION_MASK_BASE = 15; // 0xf
@@ -12636,7 +12646,6 @@
field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_TIMESTAMP_CALIBRATION;
field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_WHITE_LEVEL;
field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_MAX_ANALOG_SENSITIVITY;
- field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_NOISE_PROFILE;
field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_ORIENTATION;
field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_REFERENCE_ILLUMINANT1;
field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_REFERENCE_ILLUMINANT2;
@@ -13010,6 +13019,7 @@
field public static final android.hardware.camera2.CaptureResult.Key SENSOR_FRAME_DURATION;
field public static final android.hardware.camera2.CaptureResult.Key SENSOR_GREEN_SPLIT;
field public static final android.hardware.camera2.CaptureResult.Key SENSOR_NEUTRAL_COLOR_POINT;
+ field public static final android.hardware.camera2.CaptureResult.Key SENSOR_NOISE_PROFILE;
field public static final android.hardware.camera2.CaptureResult.Key SENSOR_ROLLING_SHUTTER_SKEW;
field public static final android.hardware.camera2.CaptureResult.Key SENSOR_SENSITIVITY;
field public static final android.hardware.camera2.CaptureResult.Key SENSOR_TEST_PATTERN_DATA;
@@ -14971,7 +14981,14 @@
method public final void setInteger(java.lang.String, int);
method public final void setLong(java.lang.String, long);
method public final void setString(java.lang.String, java.lang.String);
+ field public static final java.lang.String KEY_AAC_DRC_ATTENUATION_FACTOR = "aac-drc-cut-level";
+ field public static final java.lang.String KEY_AAC_DRC_BOOST_FACTOR = "aac-drc-boost-level";
+ field public static final java.lang.String KEY_AAC_DRC_HEAVY_COMPRESSION = "aac-drc-heavy-compression";
+ field public static final java.lang.String KEY_AAC_DRC_TARGET_REFERENCE_LEVEL = "aac-target-ref-level";
+ field public static final java.lang.String KEY_AAC_ENCODED_TARGET_LEVEL = "aac-encoded-target-level";
+ field public static final java.lang.String KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT = "aac-max-output-channel_count";
field public static final java.lang.String KEY_AAC_PROFILE = "aac-profile";
+ field public static final java.lang.String KEY_AAC_SBR_MODE = "aac-sbr-mode";
field public static final java.lang.String KEY_BITRATE_MODE = "bitrate-mode";
field public static final java.lang.String KEY_BIT_RATE = "bitrate";
field public static final java.lang.String KEY_CHANNEL_COUNT = "channel-count";
@@ -14999,6 +15016,7 @@
field public static final java.lang.String KEY_REFERENCE_CLOCK_ID = "reference-clock-id";
field public static final java.lang.String KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
field public static final java.lang.String KEY_SAMPLE_RATE = "sample-rate";
+ field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema";
field public static final java.lang.String KEY_WIDTH = "width";
field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3";
@@ -15069,7 +15087,7 @@
method public android.media.MediaMetadata.Builder putString(java.lang.String, java.lang.String);
}
- public abstract class MediaMetadataEditor {
+ public abstract deprecated class MediaMetadataEditor {
method public synchronized void addEditableKey(int);
method public abstract void apply();
method public synchronized void clear();
@@ -15539,7 +15557,7 @@
field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
}
- public class RemoteControlClient {
+ public deprecated class RemoteControlClient {
ctor public RemoteControlClient(android.app.PendingIntent);
ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper);
method public android.media.RemoteControlClient.MetadataEditor editMetadata(boolean);
@@ -15571,7 +15589,7 @@
field public static final int PLAYSTATE_STOPPED = 1; // 0x1
}
- public class RemoteControlClient.MetadataEditor extends android.media.MediaMetadataEditor {
+ public deprecated class RemoteControlClient.MetadataEditor extends android.media.MediaMetadataEditor {
method public synchronized void apply();
field public static final int BITMAP_KEY_ARTWORK = 100; // 0x64
}
@@ -15588,7 +15606,7 @@
method public abstract void onPlaybackPositionUpdate(long);
}
- public final class RemoteController {
+ public final deprecated class RemoteController {
ctor public RemoteController(android.content.Context, android.media.RemoteController.OnClientUpdateListener) throws java.lang.IllegalArgumentException;
ctor public RemoteController(android.content.Context, android.media.RemoteController.OnClientUpdateListener, android.os.Looper) throws java.lang.IllegalArgumentException;
method public boolean clearArtworkConfiguration();
@@ -15667,7 +15685,7 @@
}
public class SoundPool {
- ctor public SoundPool(int, int, int);
+ ctor public deprecated SoundPool(int, int, int);
method public final void autoPause();
method public final void autoResume();
method public int load(java.lang.String, int);
@@ -15687,6 +15705,13 @@
method public final boolean unload(int);
}
+ public static class SoundPool.Builder {
+ ctor public SoundPool.Builder();
+ method public android.media.SoundPool build();
+ method public android.media.SoundPool.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
+ method public android.media.SoundPool.Builder setMaxStreams(int) throws java.lang.IllegalArgumentException;
+ }
+
public static abstract interface SoundPool.OnLoadCompleteListener {
method public abstract void onLoadComplete(android.media.SoundPool, int, int);
}
@@ -16135,6 +16160,92 @@
}
+package android.media.browse {
+
+ public final class MediaBrowser {
+ ctor public MediaBrowser(android.content.Context, android.content.ComponentName, android.media.browse.MediaBrowser.ConnectionCallback, android.os.Bundle);
+ method public void connect();
+ method public void disconnect();
+ method public android.os.Bundle getExtras();
+ method public android.net.Uri getRoot();
+ method public android.media.session.MediaSession.Token getSessionToken();
+ method public boolean isConnected();
+ method public void loadThumbnail(android.net.Uri, int, int, android.media.browse.MediaBrowser.ThumbnailCallback);
+ method public void subscribe(android.net.Uri, android.media.browse.MediaBrowser.SubscriptionCallback);
+ method public void unsubscribe(android.net.Uri);
+ }
+
+ public static class MediaBrowser.ConnectionCallback {
+ ctor public MediaBrowser.ConnectionCallback();
+ method public void onConnected();
+ method public void onConnectionFailed();
+ method public void onConnectionSuspended();
+ }
+
+ public static abstract class MediaBrowser.SubscriptionCallback {
+ ctor public MediaBrowser.SubscriptionCallback();
+ method public void onChildrenLoaded(android.net.Uri, java.util.List<android.media.browse.MediaBrowserItem>);
+ method public void onError(android.net.Uri);
+ }
+
+ public static abstract class MediaBrowser.ThumbnailCallback {
+ ctor public MediaBrowser.ThumbnailCallback();
+ method public void onError(android.net.Uri);
+ method public void onThumbnailLoaded(android.net.Uri, android.graphics.Bitmap);
+ }
+
+ public final class MediaBrowserItem implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getExtras();
+ method public int getFlags();
+ method public int getIconResourceId();
+ method public android.net.Uri getIconUri();
+ method public java.lang.CharSequence getSummary();
+ method public java.lang.CharSequence getTitle();
+ method public android.net.Uri getUri();
+ method public boolean isBrowsable();
+ method public boolean isPlayable();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int FLAG_BROWSABLE = 1; // 0x1
+ field public static final int FLAG_PLAYABLE = 2; // 0x2
+ }
+
+ public static final class MediaBrowserItem.Builder {
+ ctor public MediaBrowserItem.Builder(android.net.Uri, int, java.lang.CharSequence);
+ method public android.media.browse.MediaBrowserItem build();
+ method public android.media.browse.MediaBrowserItem.Builder setExtras(android.os.Bundle);
+ method public android.media.browse.MediaBrowserItem.Builder setIconResourceId(int);
+ method public android.media.browse.MediaBrowserItem.Builder setIconUri(android.net.Uri);
+ method public android.media.browse.MediaBrowserItem.Builder setSummary(java.lang.CharSequence);
+ }
+
+ public abstract class MediaBrowserService extends android.app.Service {
+ ctor public MediaBrowserService();
+ method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+ method public android.media.session.MediaSession.Token getSessionToken();
+ method public void notifyChildrenChanged(android.net.Uri);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract android.media.browse.MediaBrowserService.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
+ method protected abstract void onLoadChildren(android.net.Uri, android.media.browse.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowserItem>>);
+ method protected abstract void onLoadThumbnail(android.net.Uri, int, int, android.media.browse.MediaBrowserService.Result<android.graphics.Bitmap>);
+ method public void setSessionToken(android.media.session.MediaSession.Token);
+ field public static final java.lang.String SERVICE_ACTION = "android.media.browse.MediaBrowserService";
+ }
+
+ public static final class MediaBrowserService.BrowserRoot {
+ ctor public MediaBrowserService.BrowserRoot(android.net.Uri, android.os.Bundle);
+ method public android.os.Bundle getExtras();
+ method public android.net.Uri getRootUri();
+ }
+
+ public class MediaBrowserService.Result {
+ method public void detach();
+ method public void sendResult(T);
+ }
+
+}
+
package android.media.effect {
public abstract class Effect {
@@ -16459,18 +16570,22 @@
method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
method public android.media.MediaMetadata getMetadata();
method public android.media.session.PlaybackState getPlaybackState();
+ method public java.util.List<android.media.session.MediaSession.Track> getQueue();
method public int getRatingType();
method public android.media.session.MediaController.TransportControls getTransportControls();
method public android.media.session.MediaController.VolumeInfo getVolumeInfo();
method public void removeCallback(android.media.session.MediaController.Callback);
- method public void sendControlCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+ method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void setVolumeTo(int, int);
}
public static abstract class MediaController.Callback {
ctor public MediaController.Callback();
+ method public void onExtrasChanged(android.os.Bundle);
method public void onMetadataChanged(android.media.MediaMetadata);
method public void onPlaybackStateChanged(android.media.session.PlaybackState);
+ method public void onQueueChanged(java.util.List<android.media.session.MediaSession.Track>);
+ method public void onQueueTitleChanged(java.lang.CharSequence);
method public void onSessionEvent(java.lang.String, android.os.Bundle);
method public void onVolumeInfoChanged(android.media.session.MediaController.VolumeInfo);
}
@@ -16479,16 +16594,21 @@
method public void fastForward();
method public void pause();
method public void play();
+ method public void playFromSearch(java.lang.String, android.os.Bundle);
+ method public void playUri(android.net.Uri, android.os.Bundle);
method public void rewind();
method public void seekTo(long);
+ method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
+ method public void sendCustomAction(java.lang.String, android.os.Bundle);
method public void setRating(android.media.Rating);
method public void skipToNext();
method public void skipToPrevious();
+ method public void skipToTrack(long);
method public void stop();
}
public static final class MediaController.VolumeInfo {
- method public int getAudioStream();
+ method public android.media.AudioAttributes getAudioAttributes();
method public int getCurrentVolume();
method public int getMaxVolume();
method public int getVolumeControl();
@@ -16508,13 +16628,16 @@
method public void removeTransportControlsCallback(android.media.session.MediaSession.TransportControlsCallback);
method public void sendSessionEvent(java.lang.String, android.os.Bundle);
method public void setActive(boolean);
+ method public void setExtras(android.os.Bundle);
method public void setFlags(int);
method public void setLaunchPendingIntent(android.app.PendingIntent);
method public void setMediaRouter(android.media.routing.MediaRouter);
method public void setMetadata(android.media.MediaMetadata);
method public void setPlaybackState(android.media.session.PlaybackState);
- method public void setPlaybackToLocal(int);
+ method public void setPlaybackToLocal(android.media.AudioAttributes);
method public void setPlaybackToRemote(android.media.VolumeProvider);
+ method public void setQueue(java.util.List<android.media.session.MediaSession.Track>);
+ method public void setQueueTitle(java.lang.CharSequence);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
@@ -16523,7 +16646,7 @@
public static abstract class MediaSession.Callback {
ctor public MediaSession.Callback();
- method public void onControlCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+ method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void onMediaButtonEvent(android.content.Intent);
}
@@ -16533,16 +16656,37 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
+ public static final class MediaSession.Track implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getExtras();
+ method public long getId();
+ method public android.media.MediaMetadata getMetadata();
+ method public android.net.Uri getUri();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int UNKNOWN_ID = -1; // 0xffffffff
+ }
+
+ public static final class MediaSession.Track.Builder {
+ ctor public MediaSession.Track.Builder(android.media.MediaMetadata, long, android.net.Uri);
+ method public android.media.session.MediaSession.Track build();
+ method public android.media.session.MediaSession.Track.Builder setExtras(android.os.Bundle);
+ }
+
public static abstract class MediaSession.TransportControlsCallback {
ctor public MediaSession.TransportControlsCallback();
+ method public void onCustomAction(java.lang.String, android.os.Bundle);
method public void onFastForward();
method public void onPause();
method public void onPlay();
+ method public void onPlayFromSearch(java.lang.String, android.os.Bundle);
+ method public void onPlayUri(android.net.Uri, android.os.Bundle);
method public void onRewind();
method public void onSeekTo(long);
method public void onSetRating(android.media.Rating);
method public void onSkipToNext();
method public void onSkipToPrevious();
+ method public void onSkipToTrack(long);
method public void onStop();
}
@@ -16560,6 +16704,7 @@
method public int describeContents();
method public long getActions();
method public long getBufferPosition();
+ method public java.util.List<android.media.session.PlaybackState.CustomAction> getCustomActions();
method public java.lang.CharSequence getErrorMessage();
method public long getLastPositionUpdateTime();
method public float getPlaybackSpeed();
@@ -16569,12 +16714,15 @@
field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
field public static final long ACTION_PAUSE = 2L; // 0x2L
field public static final long ACTION_PLAY = 4L; // 0x4L
+ field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
+ field public static final long ACTION_PLAY_URI = 1024L; // 0x400L
field public static final long ACTION_REWIND = 8L; // 0x8L
field public static final long ACTION_SEEK_TO = 256L; // 0x100L
field public static final long ACTION_SET_RATING = 128L; // 0x80L
field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
+ field public static final long ACTION_SKIP_TO_TRACK = 4096L; // 0x1000L
field public static final long ACTION_STOP = 1L; // 0x1L
field public static final android.os.Parcelable.Creator CREATOR;
field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
@@ -16594,14 +16742,33 @@
public static final class PlaybackState.Builder {
ctor public PlaybackState.Builder();
ctor public PlaybackState.Builder(android.media.session.PlaybackState);
+ method public android.media.session.PlaybackState.Builder addCustomAction(java.lang.String, java.lang.String, int);
+ method public android.media.session.PlaybackState.Builder addCustomAction(android.media.session.PlaybackState.CustomAction);
method public android.media.session.PlaybackState build();
method public android.media.session.PlaybackState.Builder setActions(long);
+ method public android.media.session.PlaybackState.Builder setActiveTrack(long);
method public android.media.session.PlaybackState.Builder setBufferPosition(long);
method public android.media.session.PlaybackState.Builder setErrorMessage(java.lang.CharSequence);
method public android.media.session.PlaybackState.Builder setState(int, long, float, long);
method public android.media.session.PlaybackState.Builder setState(int, long, float);
}
+ public static final class PlaybackState.CustomAction implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getAction();
+ method public android.os.Bundle getExtras();
+ method public int getIcon();
+ method public java.lang.CharSequence getName();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static final class PlaybackState.CustomAction.Builder {
+ ctor public PlaybackState.CustomAction.Builder(java.lang.String, java.lang.CharSequence, int);
+ method public android.media.session.PlaybackState.CustomAction build();
+ method public android.media.session.PlaybackState.CustomAction.Builder setExtras(android.os.Bundle);
+ }
+
}
package android.media.tv {
@@ -16670,33 +16837,33 @@
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
field public static final android.net.Uri CONTENT_URI;
- field public static final int SERVICE_TYPE_AUDIO = 2; // 0x2
- field public static final int SERVICE_TYPE_AUDIO_VIDEO = 1; // 0x1
- field public static final int SERVICE_TYPE_OTHER = 0; // 0x0
- field public static final int TYPE_1SEG = 197632; // 0x30400
- field public static final int TYPE_ATSC_C = 131584; // 0x20200
- field public static final int TYPE_ATSC_M_H = 131840; // 0x20300
- field public static final int TYPE_ATSC_T = 131072; // 0x20000
- field public static final int TYPE_CMMB = 262400; // 0x40100
- field public static final int TYPE_DTMB = 262144; // 0x40000
- field public static final int TYPE_DVB_C = 66048; // 0x10200
- field public static final int TYPE_DVB_C2 = 66049; // 0x10201
- field public static final int TYPE_DVB_H = 66304; // 0x10300
- field public static final int TYPE_DVB_S = 65792; // 0x10100
- field public static final int TYPE_DVB_S2 = 65793; // 0x10101
- field public static final int TYPE_DVB_SH = 66560; // 0x10400
- field public static final int TYPE_DVB_T = 65536; // 0x10000
- field public static final int TYPE_DVB_T2 = 65537; // 0x10001
- field public static final int TYPE_ISDB_C = 197376; // 0x30300
- field public static final int TYPE_ISDB_S = 197120; // 0x30200
- field public static final int TYPE_ISDB_T = 196608; // 0x30000
- field public static final int TYPE_ISDB_TB = 196864; // 0x30100
- field public static final int TYPE_NTSC = 1; // 0x1
- field public static final int TYPE_OTHER = 0; // 0x0
- field public static final int TYPE_PAL = 2; // 0x2
- field public static final int TYPE_SECAM = 3; // 0x3
- field public static final int TYPE_S_DMB = 327936; // 0x50100
- field public static final int TYPE_T_DMB = 327680; // 0x50000
+ field public static final java.lang.String SERVICE_TYPE_AUDIO = "SERVICE_TYPE_AUDIO";
+ field public static final java.lang.String SERVICE_TYPE_AUDIO_VIDEO = "SERVICE_TYPE_AUDIO_VIDEO";
+ field public static final java.lang.String SERVICE_TYPE_OTHER = "SERVICE_TYPE_OTHER";
+ field public static final java.lang.String TYPE_1SEG = "TYPE_1SEG";
+ field public static final java.lang.String TYPE_ATSC_C = "TYPE_ATSC_C";
+ field public static final java.lang.String TYPE_ATSC_M_H = "TYPE_ATSC_M_H";
+ field public static final java.lang.String TYPE_ATSC_T = "TYPE_ATSC_T";
+ field public static final java.lang.String TYPE_CMMB = "TYPE_CMMB";
+ field public static final java.lang.String TYPE_DTMB = "TYPE_DTMB";
+ field public static final java.lang.String TYPE_DVB_C = "TYPE_DVB_C";
+ field public static final java.lang.String TYPE_DVB_C2 = "TYPE_DVB_C2";
+ field public static final java.lang.String TYPE_DVB_H = "TYPE_DVB_H";
+ field public static final java.lang.String TYPE_DVB_S = "TYPE_DVB_S";
+ field public static final java.lang.String TYPE_DVB_S2 = "TYPE_DVB_S2";
+ field public static final java.lang.String TYPE_DVB_SH = "TYPE_DVB_SH";
+ field public static final java.lang.String TYPE_DVB_T = "TYPE_DVB_T";
+ field public static final java.lang.String TYPE_DVB_T2 = "TYPE_DVB_T2";
+ field public static final java.lang.String TYPE_ISDB_C = "TYPE_ISDB_C";
+ field public static final java.lang.String TYPE_ISDB_S = "TYPE_ISDB_S";
+ field public static final java.lang.String TYPE_ISDB_T = "TYPE_ISDB_T";
+ field public static final java.lang.String TYPE_ISDB_TB = "TYPE_ISDB_TB";
+ field public static final java.lang.String TYPE_NTSC = "TYPE_NTSC";
+ field public static final java.lang.String TYPE_OTHER = "TYPE_OTHER";
+ field public static final java.lang.String TYPE_PAL = "TYPE_PAL";
+ field public static final java.lang.String TYPE_SECAM = "TYPE_SECAM";
+ field public static final java.lang.String TYPE_S_DMB = "TYPE_S_DMB";
+ field public static final java.lang.String TYPE_T_DMB = "TYPE_T_DMB";
field public static final java.lang.String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I";
field public static final java.lang.String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P";
field public static final java.lang.String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P";
@@ -16726,9 +16893,12 @@
field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+ field public static final java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
+ field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+ field public static final java.lang.String COLUMN_SEASON_NUMBER = "season_number";
field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
@@ -16770,17 +16940,16 @@
method public java.lang.CharSequence loadLabel(android.content.Context);
method public void writeToParcel(android.os.Parcel, int);
field public static final java.lang.String EXTRA_INPUT_ID = "inputId";
- field public static final int TYPE_COMPONENT = 6; // 0x6
- field public static final int TYPE_COMPOSITE = 3; // 0x3
- field public static final int TYPE_DISPLAY_PORT = 10; // 0xa
- field public static final int TYPE_DVI = 8; // 0x8
- field public static final int TYPE_HDMI = 9; // 0x9
- field public static final int TYPE_OTHER_HARDWARE = 1; // 0x1
- field public static final int TYPE_SCART = 5; // 0x5
- field public static final int TYPE_SVIDEO = 4; // 0x4
- field public static final int TYPE_TUNER = 2; // 0x2
- field public static final int TYPE_VGA = 7; // 0x7
- field public static final int TYPE_VIRTUAL = 0; // 0x0
+ field public static final int TYPE_COMPONENT = 1004; // 0x3ec
+ field public static final int TYPE_COMPOSITE = 1001; // 0x3e9
+ field public static final int TYPE_DISPLAY_PORT = 1008; // 0x3f0
+ field public static final int TYPE_DVI = 1006; // 0x3ee
+ field public static final int TYPE_HDMI = 1007; // 0x3ef
+ field public static final int TYPE_OTHER = 1000; // 0x3e8
+ field public static final int TYPE_SCART = 1003; // 0x3eb
+ field public static final int TYPE_SVIDEO = 1002; // 0x3ea
+ field public static final int TYPE_TUNER = 0; // 0x0
+ field public static final int TYPE_VGA = 1005; // 0x3ed
}
public final class TvInputManager {
@@ -17355,18 +17524,6 @@
method public android.net.NetworkRequest.Builder setNetworkSpecifier(java.lang.String);
}
- public abstract interface PSKKeyManager {
- method public abstract java.lang.String chooseClientKeyIdentity(java.lang.String, java.net.Socket);
- method public abstract java.lang.String chooseClientKeyIdentity(java.lang.String, javax.net.ssl.SSLEngine);
- method public abstract java.lang.String chooseServerKeyIdentityHint(java.net.Socket);
- method public abstract java.lang.String chooseServerKeyIdentityHint(javax.net.ssl.SSLEngine);
- method public abstract javax.crypto.SecretKey getKey(java.lang.String, java.lang.String, java.net.Socket);
- method public abstract javax.crypto.SecretKey getKey(java.lang.String, java.lang.String, javax.net.ssl.SSLEngine);
- field public static final int MAX_IDENTITY_HINT_LENGTH_BYTES = 128; // 0x80
- field public static final int MAX_IDENTITY_LENGTH_BYTES = 128; // 0x80
- field public static final int MAX_KEY_LENGTH_BYTES = 256; // 0x100
- }
-
public class ParseException extends java.lang.RuntimeException {
field public java.lang.String response;
}
@@ -17393,6 +17550,19 @@
method public void writeToParcel(android.os.Parcel, int);
}
+ public abstract class PskKeyManager {
+ ctor public PskKeyManager();
+ method public java.lang.String chooseClientKeyIdentity(java.lang.String, java.net.Socket);
+ method public java.lang.String chooseClientKeyIdentity(java.lang.String, javax.net.ssl.SSLEngine);
+ method public java.lang.String chooseServerKeyIdentityHint(java.net.Socket);
+ method public java.lang.String chooseServerKeyIdentityHint(javax.net.ssl.SSLEngine);
+ method public javax.crypto.SecretKey getKey(java.lang.String, java.lang.String, java.net.Socket);
+ method public javax.crypto.SecretKey getKey(java.lang.String, java.lang.String, javax.net.ssl.SSLEngine);
+ field public static final int MAX_IDENTITY_HINT_LENGTH_BYTES = 128; // 0x80
+ field public static final int MAX_IDENTITY_LENGTH_BYTES = 128; // 0x80
+ field public static final int MAX_KEY_LENGTH_BYTES = 256; // 0x100
+ }
+
public final class RouteInfo implements android.os.Parcelable {
method public int describeContents();
method public android.net.IpPrefix getDestination();
@@ -22561,6 +22731,7 @@
field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
field public static final java.lang.String DISALLOW_CREATE_WINDOWS = "no_create_windows";
+ field public static final java.lang.String DISALLOW_CROSS_PROFILE_COPY_PASTE = "no_cross_profile_copy_paste";
field public static final java.lang.String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
field public static final java.lang.String DISALLOW_FACTORY_RESET = "no_factory_reset";
field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps";
@@ -23792,6 +23963,7 @@
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
+ field public static final java.lang.String TRANSCRIPTION = "transcription";
field public static final java.lang.String TYPE = "type";
field public static final int VOICEMAIL_TYPE = 4; // 0x4
field public static final java.lang.String VOICEMAIL_URI = "voicemail_uri";
@@ -27132,14 +27304,19 @@
method public final void cancelNotification(java.lang.String);
method public final void cancelNotifications(java.lang.String[]);
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
+ method public final int getCurrentListenerFlags();
method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
method public android.os.IBinder onBind(android.content.Intent);
method public void onListenerConnected();
+ method public void onListenerFlagsChanged(int);
method public void onNotificationPosted(android.service.notification.StatusBarNotification);
method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
method public void onNotificationRemoved(android.service.notification.StatusBarNotification);
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
+ method public final void requestListenerFlags(int);
+ field public static final int FLAG_DISABLE_HOST_ALERTS = 1; // 0x1
+ field public static final int FLAG_NONE = 0; // 0x0
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
}
@@ -27164,6 +27341,7 @@
ctor public StatusBarNotification(android.os.Parcel);
method public android.service.notification.StatusBarNotification clone();
method public int describeContents();
+ method public java.lang.String getGroupKey();
method public int getId();
method public java.lang.String getKey();
method public android.app.Notification getNotification();
@@ -27220,15 +27398,10 @@
package android.service.voice {
public class AlwaysOnHotwordDetector {
- method public int getAvailability();
method public android.content.Intent getManageIntent(int);
method public int getSupportedRecognitionModes();
method public int startRecognition(int);
method public int stopRecognition();
- field public static final int KEYPHRASE_ENROLLED = 2; // 0x2
- field public static final int KEYPHRASE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
- field public static final int KEYPHRASE_UNENROLLED = 1; // 0x1
- field public static final int KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
field public static final int MANAGE_ACTION_ENROLL = 0; // 0x0
field public static final int MANAGE_ACTION_RE_ENROLL = 1; // 0x1
field public static final int MANAGE_ACTION_UN_ENROLL = 2; // 0x2
@@ -27236,21 +27409,28 @@
field public static final int RECOGNITION_FLAG_NONE = 0; // 0x0
field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1
+ field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
+ field public static final int STATE_INVALID = -3; // 0xfffffffd
+ field public static final int STATE_KEYPHRASE_ENROLLED = 2; // 0x2
+ field public static final int STATE_KEYPHRASE_UNENROLLED = 1; // 0x1
+ field public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
field public static final int STATUS_ERROR = -2147483648; // 0x80000000
field public static final int STATUS_OK = 0; // 0x0
}
public static abstract interface AlwaysOnHotwordDetector.Callback {
+ method public abstract void onAvailabilityChanged(int);
method public abstract void onDetected(byte[]);
method public abstract void onDetectionStopped();
}
public class VoiceInteractionService extends android.app.Service {
ctor public VoiceInteractionService();
- method public final android.service.voice.AlwaysOnHotwordDetector getAlwaysOnHotwordDetector(java.lang.String, java.lang.String, android.service.voice.AlwaysOnHotwordDetector.Callback);
+ method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(java.lang.String, java.lang.String, android.service.voice.AlwaysOnHotwordDetector.Callback);
method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
method public android.os.IBinder onBind(android.content.Intent);
method public void onReady();
+ method public void onShutdown();
method public void startSession(android.os.Bundle);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction";
@@ -28019,6 +28199,8 @@
field public static final int PROT_NONE;
field public static final int PROT_READ;
field public static final int PROT_WRITE;
+ field public static final int PR_GET_DUMPABLE;
+ field public static final int PR_SET_DUMPABLE;
field public static final int PR_SET_NO_NEW_PRIVS;
field public static final int RT_SCOPE_HOST;
field public static final int RT_SCOPE_LINK;
@@ -28395,7 +28577,7 @@
}
public abstract class CallVideoClient {
- ctor protected CallVideoClient();
+ ctor public CallVideoClient();
method public abstract void onHandleCallSessionEvent(int);
method public abstract void onHandleCameraCapabilitiesChange(android.telecomm.CallCameraCapabilities);
method public abstract void onReceiveSessionModifyRequest(android.telecomm.VideoCallProfile);
@@ -28412,7 +28594,7 @@
}
public abstract class CallVideoProvider {
- ctor protected CallVideoProvider();
+ ctor public CallVideoProvider();
method public abstract void onRequestCallDataUsage();
method public abstract void onRequestCameraCapabilities();
method public abstract void onSendSessionModifyRequest(android.telecomm.VideoCallProfile);
@@ -28427,7 +28609,8 @@
}
public abstract class Connection {
- ctor protected Connection();
+ ctor public Connection();
+ method public final void destroy();
method public final boolean getAudioModeIsVoip();
method public final android.telecomm.CallAudioState getCallAudioState();
method public final int getCallCapabilities();
@@ -28443,27 +28626,26 @@
method public final int getVideoState();
method public final boolean isConferenceConnection();
method public final boolean isRequestingRingback();
- method protected void onAbort();
- method protected void onAnswer(int);
- method protected void onChildrenChanged(java.util.List<android.telecomm.Connection>);
- method protected void onDisconnect();
- method protected void onHold();
- method protected void onPhoneAccountClicked();
- method protected void onPlayDtmfTone(char);
- method protected void onPostDialContinue(boolean);
- method protected void onReject();
- method protected void onSeparate();
- method protected void onSetAudioState(android.telecomm.CallAudioState);
- method protected void onSetState(int);
- method protected void onStopDtmfTone();
- method protected void onSwapWithBackgroundCall();
- method protected void onUnhold();
+ method public void onAbort();
+ method public void onAnswer(int);
+ method public void onChildrenChanged(java.util.List<android.telecomm.Connection>);
+ method public void onDisconnect();
+ method public void onHold();
+ method public void onPhoneAccountClicked();
+ method public void onPlayDtmfTone(char);
+ method public void onPostDialContinue(boolean);
+ method public void onReject();
+ method public void onSeparate();
+ method public void onSetAudioState(android.telecomm.CallAudioState);
+ method public void onSetState(int);
+ method public void onStopDtmfTone();
+ method public void onSwapWithBackgroundCall();
+ method public void onUnhold();
method public final void setActive();
method public final void setAudioModeIsVoip(boolean);
method public final void setCallCapabilities(int);
method public final void setCallVideoProvider(android.telecomm.CallVideoProvider);
method public final void setCallerDisplayName(java.lang.String, int);
- method public final void setDestroyed();
method public final void setDialing();
method public final void setDisconnected(int, java.lang.String);
method public final void setHandle(android.net.Uri, int);
@@ -28509,11 +28691,12 @@
method public final void lookupRemoteAccounts(android.net.Uri, android.telecomm.SimpleResponse<android.net.Uri, java.util.List<android.telecomm.PhoneAccountHandle>>);
method public final void maybeRespondToAccountLookup();
method public final android.os.IBinder onBind(android.content.Intent);
- method protected void onConnectionAdded(android.telecomm.Connection);
- method protected void onConnectionRemoved(android.telecomm.Connection);
- method protected void onCreateConferenceConnection(java.lang.String, android.telecomm.Connection, android.telecomm.Response<java.lang.String, android.telecomm.Connection>);
- method protected void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.CreateConnectionResponse<android.telecomm.Connection>);
- method protected void onCreateOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.CreateConnectionResponse<android.telecomm.Connection>);
+ method public void onConnectionAdded(android.telecomm.Connection);
+ method public void onConnectionRemoved(android.telecomm.Connection);
+ method public void onCreateConferenceConnection(java.lang.String, android.telecomm.Connection, android.telecomm.Response<java.lang.String, android.telecomm.Connection>);
+ method public void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.CreateConnectionResponse<android.telecomm.Connection>);
+ method public void onCreateOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.CreateConnectionResponse<android.telecomm.Connection>);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.telecomm.ConnectionService";
}
public static abstract interface ConnectionService.CreateConnectionResponse {
@@ -28548,28 +28731,6 @@
method public void unholdCall(java.lang.String);
}
- public final class InCallCall implements android.os.Parcelable {
- method public int describeContents();
- method public android.telecomm.PhoneAccountHandle getAccountHandle();
- method public android.telecomm.RemoteCallVideoProvider getCallVideoProvider() throws android.os.RemoteException;
- method public java.lang.String getCallerDisplayName();
- method public int getCallerDisplayNamePresentation();
- method public java.util.List<java.lang.String> getCannedSmsResponses();
- method public int getCapabilities();
- method public long getConnectTimeMillis();
- method public int getDisconnectCauseCode();
- method public java.lang.String getDisconnectCauseMsg();
- method public android.telecomm.GatewayInfo getGatewayInfo();
- method public android.net.Uri getHandle();
- method public int getHandlePresentation();
- method public java.lang.String getId();
- method public android.telecomm.CallState getState();
- method public android.telecomm.StatusHints getStatusHints();
- method public int getVideoState();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- }
-
public abstract class InCallService extends android.app.Service {
ctor public InCallService();
method public android.telecomm.Phone getPhone();
@@ -28596,19 +28757,18 @@
}
public class PhoneAccount implements android.os.Parcelable {
- ctor public PhoneAccount(android.telecomm.PhoneAccountHandle, android.net.Uri, java.lang.String, int, int, java.lang.String, java.lang.String, boolean);
+ ctor public PhoneAccount(android.telecomm.PhoneAccountHandle, android.net.Uri, java.lang.String, int, int, java.lang.CharSequence, java.lang.CharSequence, boolean);
method public int describeContents();
method public android.telecomm.PhoneAccountHandle getAccountHandle();
method public int getCapabilities();
method public android.net.Uri getHandle();
method public android.graphics.drawable.Drawable getIcon(android.content.Context);
method public int getIconResId();
- method public java.lang.String getLabel();
- method public java.lang.String getShortDescription();
+ method public java.lang.CharSequence getLabel();
+ method public java.lang.CharSequence getShortDescription();
method public java.lang.String getSubscriptionNumber();
method public boolean isVideoCallingSupported();
method public void writeToParcel(android.os.Parcel, int);
- field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2
field public static final int CAPABILITY_SIM_CALL_MANAGER = 1; // 0x1
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
field public static final android.os.Parcelable.Creator CREATOR;
@@ -28623,14 +28783,13 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
- public class RemoteCallVideoClient implements android.os.IBinder.DeathRecipient {
- method public void binderDied();
- method public void handleCallSessionEvent(int) throws android.os.RemoteException;
- method public void handleCameraCapabilitiesChange(android.telecomm.CallCameraCapabilities) throws android.os.RemoteException;
- method public void receiveSessionModifyRequest(android.telecomm.VideoCallProfile) throws android.os.RemoteException;
- method public void receiveSessionModifyResponse(int, android.telecomm.VideoCallProfile, android.telecomm.VideoCallProfile) throws android.os.RemoteException;
- method public void updateCallDataUsage(int) throws android.os.RemoteException;
- method public void updatePeerDimensions(int, int) throws android.os.RemoteException;
+ public class RemoteCallVideoClient {
+ method public void handleCallSessionEvent(int);
+ method public void handleCameraCapabilitiesChange(android.telecomm.CallCameraCapabilities);
+ method public void receiveSessionModifyRequest(android.telecomm.VideoCallProfile);
+ method public void receiveSessionModifyResponse(int, android.telecomm.VideoCallProfile, android.telecomm.VideoCallProfile);
+ method public void updateCallDataUsage(int);
+ method public void updatePeerDimensions(int, int);
}
public class RemoteCallVideoProvider {
@@ -28639,12 +28798,12 @@
method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile);
method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile);
method public void setCallVideoClient(android.telecomm.CallVideoClient);
- method public void setCamera(java.lang.String) throws android.os.RemoteException;
+ method public void setCamera(java.lang.String);
method public void setDeviceOrientation(int);
method public void setDisplaySurface(android.view.Surface);
method public void setPauseImage(java.lang.String);
method public void setPreviewSurface(android.view.Surface);
- method public void setZoom(float) throws android.os.RemoteException;
+ method public void setZoom(float);
}
public final class RemoteConnection {
@@ -28700,40 +28859,41 @@
}
public final class StatusHints implements android.os.Parcelable {
- ctor public StatusHints(android.content.ComponentName, java.lang.String, int, android.os.Bundle);
+ ctor public StatusHints(android.content.ComponentName, java.lang.CharSequence, int, android.os.Bundle);
method public int describeContents();
method public android.content.ComponentName getComponentName();
method public android.os.Bundle getExtras();
method public android.graphics.drawable.Drawable getIcon(android.content.Context);
method public int getIconId();
- method public java.lang.String getLabel();
+ method public java.lang.CharSequence getLabel();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
public class TelecommManager {
+ method public void addNewIncomingCall(android.telecomm.PhoneAccountHandle, android.os.Bundle);
method public void clearAccounts(java.lang.String);
method public android.telecomm.PhoneAccountHandle getDefaultOutgoingPhoneAccount();
method public java.util.List<android.telecomm.PhoneAccountHandle> getEnabledPhoneAccounts();
method public android.telecomm.PhoneAccount getPhoneAccount(android.telecomm.PhoneAccountHandle);
+ method public boolean hasMultipleEnabledAccounts();
method public void registerPhoneAccount(android.telecomm.PhoneAccount);
method public void unregisterPhoneAccount(android.telecomm.PhoneAccountHandle);
- field public static final java.lang.String ACTION_CONNECTION_SERVICE;
field public static final java.lang.String ACTION_CONNECTION_SERVICE_CONFIGURE = "android.intent.action.CONNECTION_SERVICE_CONFIGURE";
- field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
+ field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecomm.intent.action.SHOW_CALL_SETTINGS";
field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecomm.extra.CALL_BACK_NUMBER";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecomm.extra.CALL_DISCONNECT_CAUSE";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecomm.extra.CONNECTION_SERVICE";
- field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.intent.extra.PHONE_ACCOUNT_HANDLE";
field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.intent.extra.START_CALL_WITH_VIDEO_STATE";
}
public class VideoCallProfile implements android.os.Parcelable {
+ ctor public VideoCallProfile(int);
ctor public VideoCallProfile(int, int);
method public int describeContents();
method public int getQuality();
@@ -31379,7 +31539,7 @@
field public static final java.lang.String ARG_MONTH = "android.arg.month";
field public static final java.lang.String ARG_MULTIPLICITY = "android.arg.multiplicity";
field public static final java.lang.String ARG_NUMBER = "android.arg.number";
- field public static final java.lang.String ARG_NUMBER_PART = "android.arg.number_part";
+ field public static final java.lang.String ARG_NUMBER_PARTS = "android.arg.number_parts";
field public static final java.lang.String ARG_NUMERATOR = "android.arg.numerator";
field public static final java.lang.String ARG_PASSWORD = "android.arg.password";
field public static final java.lang.String ARG_PATH = "android.arg.path";
@@ -31482,6 +31642,19 @@
method public android.text.style.TtsSpan.DigitsBuilder setDigits(java.lang.String);
}
+ public static class TtsSpan.ElectronicBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+ ctor public TtsSpan.ElectronicBuilder();
+ method public android.text.style.TtsSpan.ElectronicBuilder setDomain(java.lang.String);
+ method public android.text.style.TtsSpan.ElectronicBuilder setEmailArguments(java.lang.String, java.lang.String);
+ method public android.text.style.TtsSpan.ElectronicBuilder setFragmentId(java.lang.String);
+ method public android.text.style.TtsSpan.ElectronicBuilder setPassword(java.lang.String);
+ method public android.text.style.TtsSpan.ElectronicBuilder setPath(java.lang.String);
+ method public android.text.style.TtsSpan.ElectronicBuilder setPort(int);
+ method public android.text.style.TtsSpan.ElectronicBuilder setProtocol(java.lang.String);
+ method public android.text.style.TtsSpan.ElectronicBuilder setQueryString(java.lang.String);
+ method public android.text.style.TtsSpan.ElectronicBuilder setUsername(java.lang.String);
+ }
+
public static class TtsSpan.FractionBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
ctor public TtsSpan.FractionBuilder();
ctor public TtsSpan.FractionBuilder(long, long, long);
@@ -31507,6 +31680,15 @@
method public android.text.style.TtsSpan.MeasureBuilder setUnit(java.lang.String);
}
+ public static class TtsSpan.MoneyBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+ ctor public TtsSpan.MoneyBuilder();
+ method public android.text.style.TtsSpan.MoneyBuilder setCurrency(java.lang.String);
+ method public android.text.style.TtsSpan.MoneyBuilder setFractionalPart(java.lang.String);
+ method public android.text.style.TtsSpan.MoneyBuilder setIntegerPart(long);
+ method public android.text.style.TtsSpan.MoneyBuilder setIntegerPart(java.lang.String);
+ method public android.text.style.TtsSpan.MoneyBuilder setQuantity(java.lang.String);
+ }
+
public static class TtsSpan.OrdinalBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
ctor public TtsSpan.OrdinalBuilder();
ctor public TtsSpan.OrdinalBuilder(long);
@@ -31523,6 +31705,14 @@
method public C setMultiplicity(java.lang.String);
}
+ public static class TtsSpan.TelephoneBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+ ctor public TtsSpan.TelephoneBuilder();
+ ctor public TtsSpan.TelephoneBuilder(java.lang.String);
+ method public android.text.style.TtsSpan.TelephoneBuilder setCountryCode(java.lang.String);
+ method public android.text.style.TtsSpan.TelephoneBuilder setExtension(java.lang.String);
+ method public android.text.style.TtsSpan.TelephoneBuilder setNumberParts(java.lang.String);
+ }
+
public static class TtsSpan.TextBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
ctor public TtsSpan.TextBuilder();
ctor public TtsSpan.TextBuilder(java.lang.String);
@@ -31865,8 +32055,8 @@
method public android.animation.Animator onDisappear(android.view.ViewGroup, android.transition.TransitionValues, int, android.transition.TransitionValues, int);
method public android.animation.Animator onDisappear(android.view.ViewGroup, android.view.View, android.transition.TransitionValues, android.transition.TransitionValues);
method public void setMode(int);
- field public static final int IN = 1; // 0x1
- field public static final int OUT = 2; // 0x2
+ field public static final int MODE_IN = 1; // 0x1
+ field public static final int MODE_OUT = 2; // 0x2
}
public abstract class VisibilityPropagation extends android.transition.TransitionPropagation {
@@ -36523,7 +36713,7 @@
method public void removeSessionCookies(android.webkit.ValueCallback<java.lang.Boolean>);
method public synchronized void setAcceptCookie(boolean);
method public static void setAcceptFileSchemeCookies(boolean);
- method public synchronized void setAcceptThirdPartyCookies(android.webkit.WebView, boolean);
+ method public void setAcceptThirdPartyCookies(android.webkit.WebView, boolean);
method public void setCookie(java.lang.String, java.lang.String);
method public void setCookie(java.lang.String, java.lang.String, android.webkit.ValueCallback<java.lang.Boolean>);
}
@@ -36589,15 +36779,15 @@
method public boolean hasMimeType(java.lang.String);
}
- public abstract interface PermissionRequest {
+ public abstract class PermissionRequest {
+ ctor public PermissionRequest();
method public abstract void deny();
method public abstract android.net.Uri getOrigin();
- method public abstract long getResources();
- method public abstract void grant(long);
- field public static final long RESOURCE_AUDIO_CAPTURE = 4L; // 0x4L
- field public static final long RESOURCE_GEOLOCATION = 1L; // 0x1L
- field public static final long RESOURCE_PROTECTED_MEDIA_ID = 8L; // 0x8L
- field public static final long RESOURCE_VIDEO_CAPTURE = 2L; // 0x2L
+ method public abstract java.lang.String[] getResources();
+ method public abstract void grant(java.lang.String[]);
+ field public static final java.lang.String RESOURCE_AUDIO_CAPTURE = "android.webkit.resource.AUDIO_CAPTURE";
+ field public static final java.lang.String RESOURCE_PROTECTED_MEDIA_ID = "android.webkit.resource.PROTECTED_MEDIA_ID";
+ field public static final java.lang.String RESOURCE_VIDEO_CAPTURE = "android.webkit.resource.VIDEO_CAPTURE";
}
public abstract interface PluginStub {
@@ -36669,23 +36859,31 @@
method public void onRequestFocus(android.webkit.WebView);
method public void onShowCustomView(android.view.View, android.webkit.WebChromeClient.CustomViewCallback);
method public deprecated void onShowCustomView(android.view.View, int, android.webkit.WebChromeClient.CustomViewCallback);
- method public boolean showFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams);
+ method public boolean onShowFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams);
}
public static abstract interface WebChromeClient.CustomViewCallback {
method public abstract void onCustomViewHidden();
}
- public static class WebChromeClient.FileChooserParams {
+ public static abstract class WebChromeClient.FileChooserParams {
ctor public WebChromeClient.FileChooserParams();
- field public static final int MODE_OPEN_FOLDER = 2; // 0x2
- field public static final int MODE_OPEN_MULTIPLE = 1; // 0x1
- field public static final int MODE_SAVE = 4; // 0x4
- field public java.lang.String acceptTypes;
- field public boolean capture;
- field public java.lang.String defaultFilename;
- field public int mode;
- field public java.lang.String title;
+ method public abstract java.lang.String[] getAcceptTypes();
+ method public abstract java.lang.String getDefaultFilename();
+ method public abstract int getMode();
+ method public abstract java.lang.CharSequence getTitle();
+ method public abstract android.webkit.WebChromeClient.UploadHelper getUploadHelper();
+ method public abstract boolean isCaptureEnabled();
+ field public static final int OPEN = 0; // 0x0
+ field public static final int OPEN_FOLDER = 2; // 0x2
+ field public static final int OPEN_MULTIPLE = 1; // 0x1
+ field public static final int SAVE = 3; // 0x3
+ }
+
+ public static abstract class WebChromeClient.UploadHelper {
+ ctor public WebChromeClient.UploadHelper();
+ method public abstract android.content.Intent buildIntent();
+ method public abstract android.net.Uri[] parseResult(int, android.content.Intent);
}
public class WebHistoryItem implements java.lang.Cloneable {
@@ -36976,7 +37174,6 @@
method public boolean pageUp(boolean);
method public void pauseTimers();
method public void postUrl(java.lang.String, byte[]);
- method public void preauthorizePermission(android.net.Uri, long);
method public void reload();
method public void removeJavascriptInterface(java.lang.String);
method public void requestFocusNodeHref(android.os.Message);
@@ -37613,9 +37810,13 @@
ctor public CheckedTextView(android.content.Context, android.util.AttributeSet, int);
ctor public CheckedTextView(android.content.Context, android.util.AttributeSet, int, int);
method public android.graphics.drawable.Drawable getCheckMarkDrawable();
+ method public android.content.res.ColorStateList getCheckMarkTint();
+ method public android.graphics.PorterDuff.Mode getCheckMarkTintMode();
method public boolean isChecked();
method public void setCheckMarkDrawable(int);
method public void setCheckMarkDrawable(android.graphics.drawable.Drawable);
+ method public void setCheckMarkTint(android.content.res.ColorStateList);
+ method public void setCheckMarkTintMode(android.graphics.PorterDuff.Mode);
method public void setChecked(boolean);
method public void toggle();
}
diff --git a/cmds/appwidget/Android.mk b/cmds/appwidget/Android.mk
new file mode 100644
index 0000000..1fb258d
--- /dev/null
+++ b/cmds/appwidget/Android.mk
@@ -0,0 +1,16 @@
+# Copyright 2014 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_MODULE := appwidget
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := appwidget
+LOCAL_SRC_FILES := appwidget
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
+
diff --git a/cmds/appwidget/MODULE_LICENSE_APACHE2 b/cmds/appwidget/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cmds/appwidget/MODULE_LICENSE_APACHE2
diff --git a/cmds/appwidget/NOTICE b/cmds/appwidget/NOTICE
new file mode 100644
index 0000000..06a9081
--- /dev/null
+++ b/cmds/appwidget/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2014, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/cmds/appwidget/appwidget b/cmds/appwidget/appwidget
new file mode 100755
index 0000000..6105009
--- /dev/null
+++ b/cmds/appwidget/appwidget
@@ -0,0 +1,5 @@
+# Script to start "appwidget" on the device, which has a very rudimentary shell.
+base=/system
+export CLASSPATH=$base/framework/appwidget.jar
+exec app_process $base/bin com.android.commands.appwidget.AppWidget "$@"
+
diff --git a/cmds/appwidget/src/com/android/commands/appwidget/AppWidget.java b/cmds/appwidget/src/com/android/commands/appwidget/AppWidget.java
new file mode 100644
index 0000000..dd45d39
--- /dev/null
+++ b/cmds/appwidget/src/com/android/commands/appwidget/AppWidget.java
@@ -0,0 +1,167 @@
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.commands.appwidget;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import com.android.internal.appwidget.IAppWidgetService;
+
+/**
+ * This class is a command line utility for manipulating app widgets. A client
+ * can grant or revoke the permission for a given package to bind app widgets.
+ */
+public class AppWidget {
+
+ private static final String USAGE =
+ "usage: adb shell appwidget [subcommand] [options]\n"
+ + "\n"
+ + "usage: adb shell appwidget grantbind --package <PACKAGE> "
+ + " [--user <USER_ID> | current]\n"
+ + " <PACKAGE> an Android package name.\n"
+ + " <USER_ID> The user id under which the package is installed.\n"
+ + " Example:\n"
+ + " # Grant the \"foo.bar.baz\" package to bind app widgets for the current user.\n"
+ + " adb shell grantbind --package foo.bar.baz --user current\n"
+ + "\n"
+ + "usage: adb shell appwidget revokebind --package <PACKAGE> "
+ + "[--user <USER_ID> | current]\n"
+ + " <PACKAGE> an Android package name.\n"
+ + " <USER_ID> The user id under which the package is installed.\n"
+ + " Example:\n"
+ + " # Revoke the permisison to bind app widgets from the \"foo.bar.baz\" package.\n"
+ + " adb shell revokebind --package foo.bar.baz --user current\n"
+ + "\n";
+
+ private static class Parser {
+ private static final String ARGUMENT_GRANT_BIND = "grantbind";
+ private static final String ARGUMENT_REVOKE_BIND = "revokebind";
+ private static final String ARGUMENT_PACKAGE = "--package";
+ private static final String ARGUMENT_USER = "--user";
+ private static final String ARGUMENT_PREFIX = "--";
+ private static final String VALUE_USER_CURRENT = "current";
+
+ private final Tokenizer mTokenizer;
+
+ public Parser(String[] args) {
+ mTokenizer = new Tokenizer(args);
+ }
+
+ public Runnable parseCommand() {
+ try {
+ String operation = mTokenizer.nextArg();
+ if (ARGUMENT_GRANT_BIND.equals(operation)) {
+ return parseSetGrantBindAppWidgetPermissionCommand(true);
+ } else if (ARGUMENT_REVOKE_BIND.equals(operation)) {
+ return parseSetGrantBindAppWidgetPermissionCommand(false);
+ } else {
+ throw new IllegalArgumentException("Unsupported operation: " + operation);
+ }
+ } catch (IllegalArgumentException iae) {
+ System.out.println(USAGE);
+ System.out.println("[ERROR] " + iae.getMessage());
+ return null;
+ }
+ }
+
+ private SetBindAppWidgetPermissionCommand parseSetGrantBindAppWidgetPermissionCommand(
+ boolean granted) {
+ String packageName = null;
+ int userId = UserHandle.USER_OWNER;
+ for (String argument; (argument = mTokenizer.nextArg()) != null;) {
+ if (ARGUMENT_PACKAGE.equals(argument)) {
+ packageName = argumentValueRequired(argument);
+ } else if (ARGUMENT_USER.equals(argument)) {
+ String user = argumentValueRequired(argument);
+ if (VALUE_USER_CURRENT.equals(user)) {
+ userId = UserHandle.USER_CURRENT;
+ } else {
+ userId = Integer.parseInt(user);
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported argument: " + argument);
+ }
+ }
+ if (packageName == null) {
+ throw new IllegalArgumentException("Package name not specified."
+ + " Did you specify --package argument?");
+ }
+ return new SetBindAppWidgetPermissionCommand(packageName, granted, userId);
+ }
+
+ private String argumentValueRequired(String argument) {
+ String value = mTokenizer.nextArg();
+ if (TextUtils.isEmpty(value) || value.startsWith(ARGUMENT_PREFIX)) {
+ throw new IllegalArgumentException("No value for argument: " + argument);
+ }
+ return value;
+ }
+ }
+
+ private static class Tokenizer {
+ private final String[] mArgs;
+ private int mNextArg;
+
+ public Tokenizer(String[] args) {
+ mArgs = args;
+ }
+
+ private String nextArg() {
+ if (mNextArg < mArgs.length) {
+ return mArgs[mNextArg++];
+ } else {
+ return null;
+ }
+ }
+ }
+
+ private static class SetBindAppWidgetPermissionCommand implements Runnable {
+ final String mPackageName;
+ final boolean mGranted;
+ final int mUserId;
+
+ public SetBindAppWidgetPermissionCommand(String packageName, boolean granted,
+ int userId) {
+ mPackageName = packageName;
+ mGranted = granted;
+ mUserId = userId;
+ }
+
+ @Override
+ public void run() {
+ IBinder binder = ServiceManager.getService(Context.APPWIDGET_SERVICE);
+ IAppWidgetService appWidgetService = IAppWidgetService.Stub.asInterface(binder);
+ try {
+ appWidgetService.setBindAppWidgetPermission(mPackageName, mGranted, mUserId);
+ } catch (RemoteException re) {
+ re.printStackTrace();
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ Parser parser = new Parser(args);
+ Runnable command = parser.parseCommand();
+ if (command != null) {
+ command.run();
+ }
+ }
+}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index d6c17ae..faf5622 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -197,13 +197,13 @@
return;
}
- if ("block".equals(op)) {
- runSetBlockedSetting(true);
+ if ("hide".equals(op)) {
+ runSetHiddenSetting(true);
return;
}
- if ("unblock".equals(op)) {
- runSetBlockedSetting(false);
+ if ("unhide".equals(op)) {
+ runSetHiddenSetting(false);
return;
}
@@ -1471,7 +1471,7 @@
}
}
- private void runSetBlockedSetting(boolean state) {
+ private void runSetHiddenSetting(boolean state) {
int userId = 0;
String option = nextOption();
if (option != null && option.equals("--user")) {
@@ -1492,9 +1492,9 @@
return;
}
try {
- mPm.setApplicationBlockedSettingAsUser(pkg, state, userId);
- System.err.println("Package " + pkg + " new blocked state: "
- + mPm.getApplicationBlockedSettingAsUser(pkg, userId));
+ mPm.setApplicationHiddenSettingAsUser(pkg, state, userId);
+ System.err.println("Package " + pkg + " new hidden state: "
+ + mPm.getApplicationHiddenSettingAsUser(pkg, userId));
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(PM_NOT_RUNNING_ERR);
@@ -1751,8 +1751,8 @@
System.err.println(" pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm disable-user [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT");
- System.err.println(" pm block [--user USER_ID] PACKAGE_OR_COMPONENT");
- System.err.println(" pm unblock [--user USER_ID] PACKAGE_OR_COMPONENT");
+ System.err.println(" pm hide [--user USER_ID] PACKAGE_OR_COMPONENT");
+ System.err.println(" pm unhide [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm grant PACKAGE PERMISSION");
System.err.println(" pm revoke PACKAGE PERMISSION");
System.err.println(" pm set-install-location [0/auto] [1/internal] [2/external]");
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index aab6e80..09b484b 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -663,6 +663,72 @@
}
/**
+ * Rename the specified {@link Account}. This is equivalent to removing
+ * the existing account and adding a new renamed account with the old
+ * account's user data.
+ *
+ * <p>It is safe to call this method from the main thread.
+ *
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
+ * and have the same UID as the account's authenticator.
+ *
+ * @param account The {@link Account} to rename
+ * @param newName String name to be associated with the account.
+ * @param callback Callback to invoke when the request completes, null for
+ * no callback
+ * @param handler {@link Handler} identifying the callback thread, null for
+ * the main thread
+ * @return An {@link AccountManagerFuture} which resolves to the Account
+ * after the name change. If successful the account's name will be the
+ * specified new name.
+ */
+ public AccountManagerFuture<Account> renameAccount(
+ final Account account,
+ final String newName,
+ AccountManagerCallback<Account> callback,
+ Handler handler) {
+ if (account == null) throw new IllegalArgumentException("account is null.");
+ if (TextUtils.isEmpty(newName)) {
+ throw new IllegalArgumentException("newName is empty or null.");
+ }
+ return new Future2Task<Account>(handler, callback) {
+ @Override
+ public void doWork() throws RemoteException {
+ mService.renameAccount(mResponse, account, newName);
+ }
+ @Override
+ public Account bundleToResult(Bundle bundle) throws AuthenticatorException {
+ String name = bundle.getString(KEY_ACCOUNT_NAME);
+ String type = bundle.getString(KEY_ACCOUNT_TYPE);
+ return new Account(name, type);
+ }
+ }.start();
+ }
+
+ /**
+ * Gets the previous name associated with the account or {@code null}, if
+ * none. This is intended so that clients of {@link
+ * #LOGIN_ACCOUNTS_CHANGED_ACTION} broadcasts can determine if an
+ * authenticator has renamed an account.
+ *
+ * <p>It is safe to call this method from the main thread.
+ *
+ * @param account The account to query for a previous name.
+ * @return The account's previous name, null if the account has never been
+ * renamed.
+ */
+ public String getPreviousName(final Account account) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ try {
+ return mService.getPreviousName(account);
+ } catch (RemoteException e) {
+ // will never happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* Removes an account from the AccountManager. Does nothing if the account
* does not exist. Does not delete the account from the server.
* The authenticator may have its own policies preventing account
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index a04875d..a133788 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -69,4 +69,10 @@
boolean addSharedAccountAsUser(in Account account, int userId);
Account[] getSharedAccountsAsUser(int userId);
boolean removeSharedAccountAsUser(in Account account, int userId);
+
+ /* Account renaming. */
+ void renameAccount(in IAccountManagerResponse response, in Account accountToRename, String newName);
+ String getPreviousName(in Account account);
+ boolean renameSharedAccountAsUser(in Account accountToRename, String newName, int userId);
+
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index fb70098..318a520 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -284,6 +284,17 @@
return true;
}
+ case START_ACTIVITY_FROM_RECENTS_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ int taskId = data.readInt();
+ Bundle options = data.readInt() == 0 ? null : Bundle.CREATOR.createFromParcel(data);
+ int result = startActivityFromRecents(taskId, options);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
+
case FINISH_ACTIVITY_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
@@ -1650,8 +1661,10 @@
String targetPkg = data.readString();
Uri uri = Uri.CREATOR.createFromParcel(data);
int mode = data.readInt();
- int userId = data.readInt();
- grantUriPermissionFromOwner(owner, fromUid, targetPkg, uri, mode, userId);
+ int sourceUserId = data.readInt();
+ int targetUserId = data.readInt();
+ grantUriPermissionFromOwner(owner, fromUid, targetPkg, uri, mode, sourceUserId,
+ targetUserId);
reply.writeNoException();
return true;
}
@@ -2482,6 +2495,24 @@
data.recycle();
return result != 0;
}
+ public int startActivityFromRecents(int taskId, Bundle options) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(taskId);
+ if (options == null) {
+ data.writeInt(0);
+ } else {
+ data.writeInt(1);
+ options.writeToParcel(data, 0);
+ }
+ mRemote.transact(START_ACTIVITY_FROM_RECENTS_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
public boolean finishActivity(IBinder token, int resultCode, Intent resultData, boolean finishTask)
throws RemoteException {
Parcel data = Parcel.obtain();
@@ -4314,7 +4345,7 @@
}
public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
- Uri uri, int mode, int userId) throws RemoteException {
+ Uri uri, int mode, int sourceUserId, int targetUserId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
@@ -4323,7 +4354,8 @@
data.writeString(targetPkg);
uri.writeToParcel(data, 0);
data.writeInt(mode);
- data.writeInt(userId);
+ data.writeInt(sourceUserId);
+ data.writeInt(targetUserId);
mRemote.transact(GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 990ea85..caadecb 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -94,6 +94,13 @@
*/
public static final int MODE_ERRORED = 2;
+ /**
+ * Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller should
+ * use its default security check. This mode is not normally used; it should only be used
+ * with appop permissions, and callers must explicitly check for it and deal with it.
+ */
+ public static final int MODE_DEFAULT = 3;
+
// when adding one of these:
// - increment _NUM_OP
// - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode
@@ -588,7 +595,7 @@
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
- AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS
+ AppOpsManager.MODE_DEFAULT, // OP_GET_USAGE_STATS
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6a51371..1cb0fd4 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1452,10 +1452,10 @@
}
@Override
- public boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked,
+ public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
UserHandle user) {
try {
- return mPM.setApplicationBlockedSettingAsUser(packageName, blocked,
+ return mPM.setApplicationHiddenSettingAsUser(packageName, hidden,
user.getIdentifier());
} catch (RemoteException re) {
// Should never happen!
@@ -1464,9 +1464,9 @@
}
@Override
- public boolean getApplicationBlockedSettingAsUser(String packageName, UserHandle user) {
+ public boolean getApplicationHiddenSettingAsUser(String packageName, UserHandle user) {
try {
- return mPM.getApplicationBlockedSettingAsUser(packageName, user.getIdentifier());
+ return mPM.getApplicationHiddenSettingAsUser(packageName, user.getIdentifier());
} catch (RemoteException re) {
// Should never happen!
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 8e3323b..175b6a5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -413,8 +413,7 @@
registerService(CONNECTIVITY_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(CONNECTIVITY_SERVICE);
- return new ConnectivityManager(IConnectivityManager.Stub.asInterface(b),
- ctx.getPackageName());
+ return new ConnectivityManager(IConnectivityManager.Stub.asInterface(b));
}});
registerService(COUNTRY_DETECTOR, new StaticServiceFetcher() {
@@ -762,9 +761,16 @@
registerService(PERSISTENT_DATA_BLOCK_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(PERSISTENT_DATA_BLOCK_SERVICE);
- return new PersistentDataBlockManager(
- IPersistentDataBlockService.Stub.asInterface(b));
- }});
+ IPersistentDataBlockService persistentDataBlockService =
+ IPersistentDataBlockService.Stub.asInterface(b);
+ if (persistentDataBlockService != null) {
+ return new PersistentDataBlockManager(persistentDataBlockService);
+ } else {
+ // not supported
+ return null;
+ }
+ }
+ });
registerService(MEDIA_PROJECTION_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index a74fbdf..d798dbb 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -136,14 +136,15 @@
Transition transition = getSharedElementExitTransition();
final ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
mSharedElementNames);
- transition.addListener(new Transition.TransitionListenerAdapter() {
+ transition.addListener(new ContinueTransitionListener() {
@Override
public void onTransitionEnd(Transition transition) {
transition.removeListener(this);
+ super.onTransitionEnd(transition);
int count = mSharedElements.size();
for (int i = 0; i < count; i++) {
View sharedElement = mSharedElements.get(i);
- ((ViewGroup)sharedElement.getParent()).suppressLayout(true);
+ ((ViewGroup) sharedElement.getParent()).suppressLayout(true);
}
}
});
@@ -221,6 +222,8 @@
if (transition != null) {
TransitionManager.beginDelayedTransition(getDecor(), transition);
mTransitioningViews.get(0).invalidate();
+ } else {
+ transitionStarted();
}
}
@@ -307,6 +310,8 @@
if (transition != null) {
TransitionManager.beginDelayedTransition(getDecor(), transition);
getDecor().invalidate();
+ } else {
+ transitionStarted();
}
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index ac29161..53c1408 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -86,6 +86,7 @@
ParcelFileDescriptor profileFd, Bundle options, int userId) throws RemoteException;
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent, Bundle options) throws RemoteException;
+ public int startActivityFromRecents(int taskId, Bundle options) throws RemoteException;
public boolean finishActivity(IBinder token, int code, Intent data, boolean finishTask)
throws RemoteException;
public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException;
@@ -331,7 +332,7 @@
public IBinder newUriPermissionOwner(String name) throws RemoteException;
public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
- Uri uri, int mode, int userId) throws RemoteException;
+ Uri uri, int mode, int sourceUserId, int targetUserId) throws RemoteException;
public void revokeUriPermissionFromOwner(IBinder owner, Uri uri,
int mode, int userId) throws RemoteException;
@@ -756,4 +757,5 @@
int IS_BG_MEDIA_PLAYING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+226;
int MEDIA_RESOURCES_RELEASED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+227;
int NOTIFY_LAUNCH_TASK_BEHIND_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+228;
+ int START_ACTIVITY_FROM_RECENTS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 229;
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 5b92538..113c533 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -59,6 +59,8 @@
void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);
ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token);
+ void requestFlagsFromListener(in INotificationListener token, int flags);
+ int getFlagsFromListener(in INotificationListener token);
ZenModeConfig getZenModeConfig();
boolean setZenModeConfig(in ZenModeConfig config);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index da59a03..5782edc 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -799,6 +799,12 @@
public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
/**
+ * {@link #extras} key: the indices of actions to be shown in the compact view,
+ * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
+ */
+ public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+
+ /**
* Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
* displayed in the heads up space.
*
@@ -3362,6 +3368,9 @@
if (mToken != null) {
extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
}
+ if (mActionsToShowInCompact != null) {
+ extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
+ }
}
private RemoteViews generateMediaActionButton(Action action) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 5f2262b..1f2f18a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -18,6 +18,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.app.Activity;
import android.content.AbstractRestrictionsProvider;
import android.content.ComponentName;
@@ -1775,6 +1776,46 @@
}
/**
+ * Called by a device/profile owner to set whether the screen capture is disabled.
+ *
+ * <p>The calling device admin must be a device or profile owner. If it is not, a
+ * security exception will be thrown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ */
+ public void setScreenCaptureDisabled(ComponentName admin, boolean disabled) {
+ if (mService != null) {
+ try {
+ mService.setScreenCaptureDisabled(admin, UserHandle.myUserId(), disabled);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Determine whether or not screen capture has been disabled by the current
+ * admin, if specified, or all admins.
+ * @param admin The name of the admin component to check, or null to check if any admins
+ * have disabled screen capture.
+ */
+ public boolean getScreenCaptureDisabled(ComponentName admin) {
+ return getScreenCaptureDisabled(admin, UserHandle.myUserId());
+ }
+
+ /** @hide per-user version */
+ public boolean getScreenCaptureDisabled(ComponentName admin, int userHandle) {
+ if (mService != null) {
+ try {
+ return mService.getScreenCaptureDisabled(admin, userHandle);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return false;
+ }
+
+ /**
* Called by an application that is administering the device to disable keyguard customizations,
* such as widgets. After setting this, keyguard features will be disabled according to the
* provided feature list.
@@ -2043,7 +2084,6 @@
/**
* @hide
- * @SystemApi
* Sets the given component as an active admin and registers the package as the profile
* owner for this user. The package must already be installed and there shouldn't be
* an existing profile owner registered for this user. Also, this method must be called
@@ -2057,6 +2097,7 @@
* @throws IllegalArgumentException if packageName is null, the package isn't installed, or
* the user has already been set up.
*/
+ @SystemApi
public boolean setActiveProfileOwner(ComponentName admin, String ownerName)
throws IllegalArgumentException {
if (mService != null) {
@@ -2523,20 +2564,20 @@
}
/**
- * Called by device or profile owner to block or unblock packages. When a package is blocked it
+ * Called by device or profile owner to hide or unhide packages. When a package is hidden it
* is unavailable for use, but the data and actual package file remain.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param packageName The name of the package to block or unblock.
- * @param blocked {@code true} if the package should be blocked, {@code false} if it should be
- * unblocked.
- * @return boolean Whether the blocked setting of the package was successfully updated.
+ * @param packageName The name of the package to hide or unhide.
+ * @param hidden {@code true} if the package should be hidden, {@code false} if it should be
+ * unhidden.
+ * @return boolean Whether the hidden setting of the package was successfully updated.
*/
- public boolean setApplicationBlocked(ComponentName admin, String packageName,
- boolean blocked) {
+ public boolean setApplicationHidden(ComponentName admin, String packageName,
+ boolean hidden) {
if (mService != null) {
try {
- return mService.setApplicationBlocked(admin, packageName, blocked);
+ return mService.setApplicationHidden(admin, packageName, hidden);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2545,20 +2586,20 @@
}
/**
- * Called by profile or device owner to block or unblock currently installed packages. This
+ * Called by profile or device owner to hide or unhide currently installed packages. This
* should only be called by a profile or device owner running within a managed profile.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param intent An intent matching the app(s) to be updated. All apps that resolve for this
* intent will be updated in the current profile.
- * @param blocked {@code true} if the packages should be blocked, {@code false} if they should
- * be unblocked.
+ * @param hidden {@code true} if the packages should be hidden, {@code false} if they should
+ * be unhidden.
* @return int The number of activities that matched the intent and were updated.
*/
- public int setApplicationsBlocked(ComponentName admin, Intent intent, boolean blocked) {
+ public int setApplicationsHidden(ComponentName admin, Intent intent, boolean hidden) {
if (mService != null) {
try {
- return mService.setApplicationsBlocked(admin, intent, blocked);
+ return mService.setApplicationsHidden(admin, intent, hidden);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2567,16 +2608,16 @@
}
/**
- * Called by device or profile owner to determine if a package is blocked.
+ * Called by device or profile owner to determine if a package is hidden.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param packageName The name of the package to retrieve the blocked status of.
- * @return boolean {@code true} if the package is blocked, {@code false} otherwise.
+ * @param packageName The name of the package to retrieve the hidden status of.
+ * @return boolean {@code true} if the package is hidden, {@code false} otherwise.
*/
- public boolean isApplicationBlocked(ComponentName admin, String packageName) {
+ public boolean isApplicationHidden(ComponentName admin, String packageName) {
if (mService != null) {
try {
- return mService.isApplicationBlocked(admin, packageName);
+ return mService.isApplicationHidden(admin, packageName);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9b1979f..6499ae4 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -88,6 +88,9 @@
void setCameraDisabled(in ComponentName who, boolean disabled, int userHandle);
boolean getCameraDisabled(in ComponentName who, int userHandle);
+ void setScreenCaptureDisabled(in ComponentName who, int userHandle, boolean disabled);
+ boolean getScreenCaptureDisabled(in ComponentName who, int userHandle);
+
void setKeyguardDisabledFeatures(in ComponentName who, int which, int userHandle);
int getKeyguardDisabledFeatures(in ComponentName who, int userHandle);
@@ -132,9 +135,9 @@
void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
void clearCrossProfileIntentFilters(in ComponentName admin);
- boolean setApplicationBlocked(in ComponentName admin, in String packageName, boolean blocked);
- int setApplicationsBlocked(in ComponentName admin, in Intent intent, boolean blocked);
- boolean isApplicationBlocked(in ComponentName admin, in String packageName);
+ boolean setApplicationHidden(in ComponentName admin, in String packageName, boolean hidden);
+ int setApplicationsHidden(in ComponentName admin, in Intent intent, boolean hidden);
+ boolean isApplicationHidden(in ComponentName admin, in String packageName);
UserHandle createUser(in ComponentName who, in String name);
UserHandle createAndInitializeUser(in ComponentName who, in String name, in String profileOwnerName, in ComponentName profileOwnerComponent, in Bundle adminExtras);
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 35ff665..57d2011 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -134,6 +134,7 @@
mLastTimeSaved = stats.mLastTimeSaved;
final int pkgCount = stats.mPackageStats.size();
+ mPackageStats.ensureCapacity(pkgCount);
for (int i = 0; i < pkgCount; i++) {
PackageUsageStats pkgStats = stats.mPackageStats.valueAt(i);
mPackageStats.append(stats.mPackageStats.keyAt(i), new PackageUsageStats(pkgStats));
diff --git a/telecomm/java/android/telecomm/InCallCall.aidl b/core/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl
similarity index 74%
copy from telecomm/java/android/telecomm/InCallCall.aidl
copy to core/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl
index be2cdf8..60cbf9f 100644
--- a/telecomm/java/android/telecomm/InCallCall.aidl
+++ b/core/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright 2014, The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.telecomm;
+package android.bluetooth;
-parcelable InCallCall;
+parcelable BluetoothActivityEnergyInfo;
diff --git a/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java
new file mode 100644
index 0000000..ce87329
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Record of energy and activity information from controller and
+ * underlying bt stack state.Timestamp the record with system
+ * time
+ * @hide
+ */
+public final class BluetoothActivityEnergyInfo implements Parcelable {
+ private final int mBluetoothStackState;
+ private final int mControllerTxTimeMs;
+ private final int mControllerRxTimeMs;
+ private final int mControllerIdleTimeMs;
+ private final int mControllerEnergyUsed;
+ private final long timestamp;
+
+ public static final int BT_STACK_STATE_INVALID = 0;
+ public static final int BT_STACK_STATE_STATE_ACTIVE = 1;
+ public static final int BT_STACK_STATE_STATE_SCANNING = 2;
+ public static final int BT_STACK_STATE_STATE_IDLE = 3;
+
+ public BluetoothActivityEnergyInfo(int stackState, int txTime, int rxTime,
+ int idleTime, int energyUsed) {
+ mBluetoothStackState = stackState;
+ mControllerTxTimeMs = txTime;
+ mControllerRxTimeMs = rxTime;
+ mControllerIdleTimeMs = idleTime;
+ mControllerEnergyUsed = energyUsed;
+ timestamp = System.currentTimeMillis();
+ }
+
+ @Override
+ public String toString() {
+ return "BluetoothActivityEnergyInfo{"
+ + " timestamp=" + timestamp
+ + " mBluetoothStackState=" + mBluetoothStackState
+ + " mControllerTxTimeMs=" + mControllerTxTimeMs
+ + " mControllerRxTimeMs=" + mControllerRxTimeMs
+ + " mControllerIdleTimeMs=" + mControllerIdleTimeMs
+ + " mControllerEnergyUsed=" + mControllerEnergyUsed
+ + " }";
+ }
+
+ public static final Parcelable.Creator<BluetoothActivityEnergyInfo> CREATOR =
+ new Parcelable.Creator<BluetoothActivityEnergyInfo>() {
+ public BluetoothActivityEnergyInfo createFromParcel(Parcel in) {
+ int stackState = in.readInt();
+ int txTime = in.readInt();
+ int rxTime = in.readInt();
+ int idleTime = in.readInt();
+ int energyUsed = in.readInt();
+ return new BluetoothActivityEnergyInfo(stackState, txTime, rxTime,
+ idleTime, energyUsed);
+ }
+ public BluetoothActivityEnergyInfo[] newArray(int size) {
+ return new BluetoothActivityEnergyInfo[size];
+ }
+ };
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mBluetoothStackState);
+ out.writeInt(mControllerTxTimeMs);
+ out.writeInt(mControllerRxTimeMs);
+ out.writeInt(mControllerIdleTimeMs);
+ out.writeInt(mControllerEnergyUsed);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @return bt stack reported state
+ */
+ public int getBluetoothStackState() {
+ return mBluetoothStackState;
+ }
+
+ /**
+ * @return tx time in ms
+ */
+ public int getControllerTxTimeMillis() {
+ return mControllerTxTimeMs;
+ }
+
+ /**
+ * @return rx time in ms
+ */
+ public int getControllerRxTimeMillis() {
+ return mControllerRxTimeMs;
+ }
+
+ /**
+ * @return idle time in ms
+ */
+ public int getControllerIdleTimeMillis() {
+ return mControllerIdleTimeMs;
+ }
+
+ /**
+ * product of current(mA), voltage(V) and time(ms)
+ * @return energy used
+ */
+ public int getControllerEnergyUsed() {
+ return mControllerEnergyUsed;
+ }
+ /**
+ * @return timestamp(wall clock) of record creation
+ */
+ public long getTimeStamp() {
+ return timestamp;
+ }
+
+ /**
+ * @return if the record is valid
+ */
+ public boolean isValid() {
+ return ((getControllerTxTimeMillis() !=0) ||
+ (getControllerRxTimeMillis() !=0) ||
+ (getControllerIdleTimeMillis() !=0));
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index faf8645..b1cbb13 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -354,29 +354,17 @@
/** The profile is in disconnecting state */
public static final int STATE_DISCONNECTING = 3;
- /** States for Bluetooth LE advertising */
- /** @hide */
- public static final int STATE_ADVERTISE_STARTING = 0;
- /** @hide */
- public static final int STATE_ADVERTISE_STARTED = 1;
- /** @hide */
- public static final int STATE_ADVERTISE_STOPPING = 2;
- /** @hide */
- public static final int STATE_ADVERTISE_STOPPED = 3;
- /**
- * Force stopping advertising without callback in case the advertising app dies.
- * @hide
- */
- public static final int STATE_ADVERTISE_FORCE_STOPPING = 4;
-
/** @hide */
public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
- /** @hide */
- public static final int ADVERTISE_CALLBACK_SUCCESS = 0;
-
private static final int ADDRESS_LENGTH = 17;
+ private static final int CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS = 30;
+ /** @hide */
+ public static final int ACTIVITY_ENERGY_INFO_CACHED = 0;
+ /** @hide */
+ public static final int ACTIVITY_ENERGY_INFO_REFRESHED = 1;
+
/**
* Lazily initialized singleton. Guaranteed final after first object
* constructed.
@@ -935,6 +923,43 @@
}
/**
+ * Return the record of {@link BluetoothActivityEnergyInfo} object that
+ * has the activity and energy info. This can be used to ascertain what
+ * the controller has been up to, since the last sample.
+ * @param updateType Type of info, cached vs refreshed.
+ *
+ * @return a record with {@link BluetoothActivityEnergyInfo} or null if
+ * report is unavailable or unsupported
+ * @hide
+ */
+ public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) {
+ if (getState() != STATE_ON) return null;
+ try {
+ BluetoothActivityEnergyInfo record;
+ if (!mService.isActivityAndEnergyReportingSupported()) {
+ return null;
+ }
+ synchronized(this) {
+ if (updateType == ACTIVITY_ENERGY_INFO_REFRESHED) {
+ mService.getActivityEnergyInfoFromController();
+ wait(CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS);
+ }
+ record = mService.reportActivityInfo();
+ if (record.isValid()) {
+ return record;
+ } else {
+ return null;
+ }
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "getControllerActivityEnergyInfoCallback wait interrupted: " + e);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e);
+ }
+ return null;
+ }
+
+ /**
* Return the set of {@link BluetoothDevice} objects that are bonded
* (paired) to the local adapter.
* <p>If Bluetooth state is not {@link #STATE_ON}, this API
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index d334b91..ca55803 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -18,6 +18,7 @@
import android.bluetooth.IBluetoothCallback;
import android.bluetooth.IBluetoothStateChangeCallback;
+import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothDevice;
import android.os.ParcelUuid;
import android.os.ParcelFileDescriptor;
@@ -88,4 +89,7 @@
boolean isMultiAdvertisementSupported();
boolean isOffloadedFilteringSupported();
boolean isOffloadedScanBatchingSupported();
+ boolean isActivityAndEnergyReportingSupported();
+ void getActivityEnergyInfoFromController();
+ BluetoothActivityEnergyInfo reportActivityInfo();
}
diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java
index f2e4828..b137eeb 100644
--- a/core/java/android/bluetooth/le/AdvertiseData.java
+++ b/core/java/android/bluetooth/le/AdvertiseData.java
@@ -25,6 +25,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
/**
* Advertise data packet container for Bluetooth LE advertising. This represents the data to be
@@ -119,13 +120,44 @@
return mIncludeDeviceName;
}
+ /**
+ * @hide
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mServiceUuids, mManufacturerId, mManufacturerSpecificData,
+ mServiceDataUuid, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ AdvertiseData other = (AdvertiseData) obj;
+ return Objects.equals(mServiceUuids, other.mServiceUuids) &&
+ mManufacturerId == other.mManufacturerId &&
+ Objects.deepEquals(mManufacturerSpecificData, other.mManufacturerSpecificData) &&
+ Objects.equals(mServiceDataUuid, other.mServiceDataUuid) &&
+ Objects.deepEquals(mServiceData, other.mServiceData) &&
+ mIncludeDeviceName == other.mIncludeDeviceName &&
+ mIncludeTxPowerLevel == other.mIncludeTxPowerLevel;
+ }
+
@Override
public String toString() {
return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerId="
+ mManufacturerId + ", mManufacturerSpecificData="
+ Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid="
+ mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData)
- + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" + "]";
+ + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName="
+ + mIncludeDeviceName + "]";
}
@Override
@@ -135,32 +167,23 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- if (mServiceUuids == null) {
- dest.writeInt(0);
- } else {
- dest.writeInt(mServiceUuids.size());
- dest.writeList(mServiceUuids);
- }
+ dest.writeList(mServiceUuids);
dest.writeInt(mManufacturerId);
if (mManufacturerSpecificData == null) {
dest.writeInt(0);
} else {
+ dest.writeInt(1);
dest.writeInt(mManufacturerSpecificData.length);
dest.writeByteArray(mManufacturerSpecificData);
}
-
- if (mServiceDataUuid == null) {
+ dest.writeParcelable(mServiceDataUuid, flags);
+ if (mServiceData == null) {
dest.writeInt(0);
} else {
dest.writeInt(1);
- dest.writeParcelable(mServiceDataUuid, flags);
- if (mServiceData == null) {
- dest.writeInt(0);
- } else {
- dest.writeInt(mServiceData.length);
- dest.writeByteArray(mServiceData);
- }
+ dest.writeInt(mServiceData.length);
+ dest.writeByteArray(mServiceData);
}
dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0));
dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0));
@@ -179,29 +202,27 @@
@Override
public AdvertiseData createFromParcel(Parcel in) {
Builder builder = new Builder();
- if (in.readInt() > 0) {
- List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
- in.readList(uuids, ParcelUuid.class.getClassLoader());
+ @SuppressWarnings("unchecked")
+ List<ParcelUuid> uuids = in.readArrayList(ParcelUuid.class.getClassLoader());
+ if (uuids != null) {
for (ParcelUuid uuid : uuids) {
builder.addServiceUuid(uuid);
}
}
int manufacturerId = in.readInt();
- int manufacturerDataLength = in.readInt();
- if (manufacturerDataLength > 0) {
+ if (in.readInt() == 1) {
+ int manufacturerDataLength = in.readInt();
byte[] manufacturerData = new byte[manufacturerDataLength];
in.readByteArray(manufacturerData);
builder.setManufacturerData(manufacturerId, manufacturerData);
}
+ ParcelUuid serviceDataUuid = in.readParcelable(
+ ParcelUuid.class.getClassLoader());
if (in.readInt() == 1) {
- ParcelUuid serviceDataUuid = in.readParcelable(
- ParcelUuid.class.getClassLoader());
int serviceDataLength = in.readInt();
- if (serviceDataLength > 0) {
- byte[] serviceData = new byte[serviceDataLength];
- in.readByteArray(serviceData);
- builder.setServiceData(serviceDataUuid, serviceData);
- }
+ byte[] serviceData = new byte[serviceDataLength];
+ in.readByteArray(serviceData);
+ builder.setServiceData(serviceDataUuid, serviceData);
}
builder.setIncludeTxPowerLevel(in.readByte() == 1);
builder.setIncludeDeviceName(in.readByte() == 1);
@@ -213,15 +234,9 @@
* Builder for {@link AdvertiseData}.
*/
public static final class Builder {
- private static final int MAX_ADVERTISING_DATA_BYTES = 31;
- // Each fields need one byte for field length and another byte for field type.
- private static final int OVERHEAD_BYTES_PER_FIELD = 2;
- // Flags field will be set by system.
- private static final int FLAGS_FIELD_BYTES = 3;
-
@Nullable
private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>();
- private int mManufacturerId;
+ private int mManufacturerId = -1;
@Nullable
private byte[] mManufacturerSpecificData;
@Nullable
@@ -307,7 +322,6 @@
/**
* Build the {@link AdvertiseData}.
- *
*/
public AdvertiseData build() {
return new AdvertiseData(mServiceUuids,
@@ -315,49 +329,5 @@
mServiceData, mManufacturerId, mManufacturerSpecificData,
mIncludeTxPowerLevel, mIncludeDeviceName);
}
-
- // Compute the size of the advertisement data.
- private int totalBytes() {
- int size = FLAGS_FIELD_BYTES; // flags field is always set.
- if (mServiceUuids != null) {
- int num16BitUuids = 0;
- int num32BitUuids = 0;
- int num128BitUuids = 0;
- for (ParcelUuid uuid : mServiceUuids) {
- if (BluetoothUuid.is16BitUuid(uuid)) {
- ++num16BitUuids;
- } else if (BluetoothUuid.is32BitUuid(uuid)) {
- ++num32BitUuids;
- } else {
- ++num128BitUuids;
- }
- }
- // 16 bit service uuids are grouped into one field when doing advertising.
- if (num16BitUuids != 0) {
- size += OVERHEAD_BYTES_PER_FIELD +
- num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
- }
- // 32 bit service uuids are grouped into one field when doing advertising.
- if (num32BitUuids != 0) {
- size += OVERHEAD_BYTES_PER_FIELD +
- num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
- }
- // 128 bit service uuids are grouped into one field when doing advertising.
- if (num128BitUuids != 0) {
- size += OVERHEAD_BYTES_PER_FIELD +
- num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
- }
- }
- if (mServiceData != null) {
- size += OVERHEAD_BYTES_PER_FIELD + mServiceData.length;
- }
- if (mManufacturerSpecificData != null) {
- size += OVERHEAD_BYTES_PER_FIELD + mManufacturerSpecificData.length;
- }
- if (mIncludeTxPowerLevel) {
- size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
- }
- return size;
- }
}
}
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index fc53afe..93d4349 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -18,6 +18,7 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothGattCallback;
import android.bluetooth.IBluetoothManager;
@@ -49,6 +50,14 @@
private static final String TAG = "BluetoothLeAdvertiser";
+ private static final int MAX_ADVERTISING_DATA_BYTES = 31;
+ // Each fields need one byte for field length and another byte for field type.
+ private static final int OVERHEAD_BYTES_PER_FIELD = 2;
+ // Flags field will be set by system.
+ private static final int FLAGS_FIELD_BYTES = 3;
+ private static final int MANUFACTURER_SPECIFIC_DATA_LENGTH = 2;
+ private static final int SERVICE_DATA_UUID_LENGTH = 2;
+
private final IBluetoothManager mBluetoothManager;
private final Handler mHandler;
private BluetoothAdapter mBluetoothAdapter;
@@ -101,6 +110,11 @@
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
}
+ if (totalBytes(advertiseData) > MAX_ADVERTISING_DATA_BYTES ||
+ totalBytes(scanResponse) > MAX_ADVERTISING_DATA_BYTES) {
+ postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE);
+ return;
+ }
if (mLeAdvertisers.containsKey(callback)) {
postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
return;
@@ -159,6 +173,62 @@
}
}
+ // Compute the size of the advertise data.
+ private int totalBytes(AdvertiseData data) {
+ if (data == null) {
+ return 0;
+ }
+ int size = FLAGS_FIELD_BYTES; // flags field is always set.
+ if (data.getServiceUuids() != null) {
+ int num16BitUuids = 0;
+ int num32BitUuids = 0;
+ int num128BitUuids = 0;
+ for (ParcelUuid uuid : data.getServiceUuids()) {
+ if (BluetoothUuid.is16BitUuid(uuid)) {
+ ++num16BitUuids;
+ } else if (BluetoothUuid.is32BitUuid(uuid)) {
+ ++num32BitUuids;
+ } else {
+ ++num128BitUuids;
+ }
+ }
+ // 16 bit service uuids are grouped into one field when doing advertising.
+ if (num16BitUuids != 0) {
+ size += OVERHEAD_BYTES_PER_FIELD +
+ num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
+ }
+ // 32 bit service uuids are grouped into one field when doing advertising.
+ if (num32BitUuids != 0) {
+ size += OVERHEAD_BYTES_PER_FIELD +
+ num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
+ }
+ // 128 bit service uuids are grouped into one field when doing advertising.
+ if (num128BitUuids != 0) {
+ size += OVERHEAD_BYTES_PER_FIELD +
+ num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
+ }
+ }
+ if (data.getServiceDataUuid() != null) {
+ size += OVERHEAD_BYTES_PER_FIELD + SERVICE_DATA_UUID_LENGTH
+ + byteLength(data.getServiceData());
+ }
+ if (data.getManufacturerId() > 0) {
+ size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH +
+ byteLength(data.getManufacturerSpecificData());
+ }
+ if (data.getIncludeTxPowerLevel()) {
+ size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
+ }
+ if (data.getIncludeDeviceName() && mBluetoothAdapter.getName() != null) {
+ size += OVERHEAD_BYTES_PER_FIELD + mBluetoothAdapter.getName().length();
+ }
+ return size;
+ }
+
+ private int byteLength(byte[] array) {
+ return array == null ? 0 : array.length;
+ }
+
/**
* Bluetooth GATT interface callbacks for advertising.
*/
diff --git a/core/java/android/content/AbstractRestrictionsProvider.java b/core/java/android/content/AbstractRestrictionsProvider.java
index 3272970..2b40870 100644
--- a/core/java/android/content/AbstractRestrictionsProvider.java
+++ b/core/java/android/content/AbstractRestrictionsProvider.java
@@ -17,8 +17,8 @@
package android.content;
import android.app.admin.DevicePolicyManager;
-import android.os.Bundle;
import android.os.IBinder;
+import android.os.PersistableBundle;
/**
* Abstract implementation of a Restrictions Provider BroadcastReceiver. To implement a
@@ -30,7 +30,7 @@
* The function of a Restrictions Provider is to transport permission requests from apps on this
* device to an administrator (most likely on a remote device or computer) and deliver back
* responses. The response should be sent back to the app via
- * {@link RestrictionsManager#notifyPermissionResponse(String, Bundle)}.
+ * {@link RestrictionsManager#notifyPermissionResponse(String, PersistableBundle)}.
*
* @see RestrictionsManager
*/
@@ -59,7 +59,7 @@
* @see RestrictionsManager#REQUEST_KEY_ID
*/
public abstract void requestPermission(Context context,
- String packageName, String requestType, Bundle request);
+ String packageName, String requestType, String requestId, PersistableBundle request);
/**
* Intercept standard Restrictions Provider broadcasts. Implementations
@@ -73,8 +73,10 @@
if (RestrictionsManager.ACTION_REQUEST_PERMISSION.equals(action)) {
String packageName = intent.getStringExtra(RestrictionsManager.EXTRA_PACKAGE_NAME);
String requestType = intent.getStringExtra(RestrictionsManager.EXTRA_REQUEST_TYPE);
- Bundle request = intent.getBundleExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE);
- requestPermission(context, packageName, requestType, request);
+ String requestId = intent.getStringExtra(RestrictionsManager.EXTRA_REQUEST_ID);
+ PersistableBundle request = (PersistableBundle)
+ intent.getParcelableExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE);
+ requestPermission(context, packageName, requestType, requestId, request);
}
}
}
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index b44abf9..7a16ef8 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -828,6 +828,26 @@
}
}
+ /**
+ * Only fixing the data field of the intents
+ * @hide
+ */
+ public void fixUrisLight(int contentUserHint) {
+ final int size = mItems.size();
+ for (int i = 0; i < size; i++) {
+ final Item item = mItems.get(i);
+ if (item.mIntent != null) {
+ Uri data = item.mIntent.getData();
+ if (data != null) {
+ item.mIntent.setData(maybeAddUserId(data, contentUserHint));
+ }
+ }
+ if (item.mUri != null) {
+ item.mUri = maybeAddUserId(item.mUri, contentUserHint);
+ }
+ }
+ }
+
@Override
public String toString() {
StringBuilder b = new StringBuilder(128);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index de3b881..87d14b9 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -2153,8 +2153,16 @@
* @param sync the master auto-sync setting that applies to all the providers and accounts
*/
public static void setMasterSyncAutomatically(boolean sync) {
+ setMasterSyncAutomaticallyAsUser(sync, UserHandle.getCallingUserId());
+ }
+
+ /**
+ * @see #setMasterSyncAutomatically(boolean)
+ * @hide
+ */
+ public static void setMasterSyncAutomaticallyAsUser(boolean sync, int userId) {
try {
- getContentService().setMasterSyncAutomatically(sync);
+ getContentService().setMasterSyncAutomaticallyAsUser(sync, userId);
} catch (RemoteException e) {
// exception ignored; if this is thrown then it means the runtime is in the midst of
// being restarted
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b7344b7..95c62e0 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2837,6 +2837,7 @@
* @see android.net.NetworkScoreManager
* @hide
*/
+ @SystemApi
public static final String NETWORK_SCORE_SERVICE = "network_score";
/**
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index d363ad1..9998f08 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -128,6 +128,7 @@
void setIsSyncable(in Account account, String providerName, int syncable);
void setMasterSyncAutomatically(boolean flag);
+ void setMasterSyncAutomaticallyAsUser(boolean flag, int userId);
boolean getMasterSyncAutomatically();
boolean getMasterSyncAutomaticallyAsUser(int userId);
diff --git a/core/java/android/content/IRestrictionsManager.aidl b/core/java/android/content/IRestrictionsManager.aidl
index b1c0a3a..495ac2e 100644
--- a/core/java/android/content/IRestrictionsManager.aidl
+++ b/core/java/android/content/IRestrictionsManager.aidl
@@ -16,7 +16,9 @@
package android.content;
+import android.content.Intent;
import android.os.Bundle;
+import android.os.PersistableBundle;
/**
* Interface used by the RestrictionsManager
@@ -25,6 +27,8 @@
interface IRestrictionsManager {
Bundle getApplicationRestrictions(in String packageName);
boolean hasRestrictionsProvider();
- void requestPermission(in String packageName, in String requestTemplate, in Bundle requestData);
- void notifyPermissionResponse(in String packageName, in Bundle response);
+ void requestPermission(in String packageName, in String requestType, in String requestId,
+ in PersistableBundle requestData);
+ void notifyPermissionResponse(in String packageName, in PersistableBundle response);
+ Intent getLocalApprovalIntent();
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3f705ac..287ea35 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2682,8 +2682,7 @@
"android.intent.action.QUICK_CLOCK";
/**
- * Broadcast Action: This is broadcast when a user action should request the
- * brightness setting dialog.
+ * Activity Action: Shows the brightness setting dialog.
* @hide
*/
public static final String ACTION_SHOW_BRIGHTNESS_DIALOG =
diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java
index 5ae10cfc..c1226c0 100644
--- a/core/java/android/content/RestrictionsManager.java
+++ b/core/java/android/content/RestrictionsManager.java
@@ -16,6 +16,7 @@
package android.content;
+import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -23,6 +24,7 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.util.AttributeSet;
import android.util.Log;
@@ -51,7 +53,7 @@
* <p>
* The RestrictionsManager forwards the dynamic requests to the active
* Restrictions Provider. The Restrictions Provider can respond back to requests by calling
- * {@link #notifyPermissionResponse(String, Bundle)}, when
+ * {@link #notifyPermissionResponse(String, PersistableBundle)}, when
* a response is received from the administrator of the device or user.
* The response is relayed back to the application via a protected broadcast,
* {@link #ACTION_PERMISSION_RESPONSE_RECEIVED}.
@@ -126,14 +128,15 @@
* {@link #EXTRA_RESPONSE_BUNDLE}.
*/
public static final String ACTION_PERMISSION_RESPONSE_RECEIVED =
- "android.intent.action.PERMISSION_RESPONSE_RECEIVED";
+ "android.content.action.PERMISSION_RESPONSE_RECEIVED";
/**
* Broadcast intent sent to the Restrictions Provider to handle a permission request from
* an app. It will have the following extras: {@link #EXTRA_PACKAGE_NAME},
- * {@link #EXTRA_REQUEST_TYPE} and {@link #EXTRA_REQUEST_BUNDLE}. The Restrictions Provider
- * will handle the request and respond back to the RestrictionsManager, when a response is
- * available, by calling {@link #notifyPermissionResponse}.
+ * {@link #EXTRA_REQUEST_TYPE}, {@link #EXTRA_REQUEST_ID} and {@link #EXTRA_REQUEST_BUNDLE}.
+ * The Restrictions Provider will handle the request and respond back to the
+ * RestrictionsManager, when a response is available, by calling
+ * {@link #notifyPermissionResponse}.
* <p>
* The BroadcastReceiver must require the {@link android.Manifest.permission#BIND_DEVICE_ADMIN}
* permission to ensure that only the system can send the broadcast.
@@ -142,17 +145,45 @@
"android.content.action.REQUEST_PERMISSION";
/**
+ * Activity intent that is optionally implemented by the Restrictions Provider package
+ * to challenge for an administrator PIN or password locally on the device. Apps will
+ * call this intent using {@link Activity#startActivityForResult}. On a successful
+ * response, {@link Activity#onActivityResult} will return a resultCode of
+ * {@link Activity#RESULT_OK}.
+ * <p>
+ * The intent must contain {@link #EXTRA_REQUEST_BUNDLE} as an extra and the bundle must
+ * contain at least {@link #REQUEST_KEY_MESSAGE} for the activity to display.
+ * <p>
+ * @see #getLocalApprovalIntent()
+ */
+ public static final String ACTION_REQUEST_LOCAL_APPROVAL =
+ "android.content.action.REQUEST_LOCAL_APPROVAL";
+
+ /**
* The package name of the application making the request.
+ * <p>
+ * Type: String
*/
public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
/**
* The request type passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
+ * <p>
+ * Type: String
*/
public static final String EXTRA_REQUEST_TYPE = "android.content.extra.REQUEST_TYPE";
/**
+ * The request ID passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
+ * <p>
+ * Type: String
+ */
+ public static final String EXTRA_REQUEST_ID = "android.content.extra.REQUEST_ID";
+
+ /**
* The request bundle passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
+ * <p>
+ * Type: {@link PersistableBundle}
*/
public static final String EXTRA_REQUEST_BUNDLE = "android.content.extra.REQUEST_BUNDLE";
@@ -163,14 +194,15 @@
* <li>{@link #REQUEST_KEY_ID}: The request ID.</li>
* <li>{@link #RESPONSE_KEY_RESULT}: The response result.</li>
* </ul>
+ * <p>
+ * Type: {@link PersistableBundle}
*/
public static final String EXTRA_RESPONSE_BUNDLE = "android.content.extra.RESPONSE_BUNDLE";
/**
* Request type for a simple question, with a possible title and icon.
* <p>
- * Required keys are
- * {@link #REQUEST_KEY_ID} and {@link #REQUEST_KEY_MESSAGE}.
+ * Required keys are: {@link #REQUEST_KEY_MESSAGE}
* <p>
* Optional keys are
* {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE},
@@ -179,22 +211,6 @@
public static final String REQUEST_TYPE_APPROVAL = "android.request.type.approval";
/**
- * Request type for a local password challenge. This is a way for an app to ask
- * the administrator to override an operation that is restricted on the device, such
- * as configuring Wi-Fi access points. It is most useful for situations where there
- * is no network connectivity for a remote administrator's response. The normal user of the
- * device is not expected to know the password. The challenge is meant for the administrator.
- * <p>
- * Required keys are
- * {@link #REQUEST_KEY_ID} and {@link #REQUEST_KEY_MESSAGE}.
- * <p>
- * Optional keys are
- * {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE},
- * {@link #REQUEST_KEY_APPROVE_LABEL} and {@link #REQUEST_KEY_DENY_LABEL}.
- */
- public static final String REQUEST_TYPE_LOCAL_APPROVAL = "android.request.type.local_approval";
-
- /**
* Key for request ID contained in the request bundle.
* <p>
* App-generated request ID to identify the specific request when receiving
@@ -240,9 +256,10 @@
* Key for request icon contained in the request bundle.
* <p>
* Optional, shown alongside the request message presented to the administrator
- * who approves the request.
+ * who approves the request. The content must be a compressed image such as a
+ * PNG or JPEG, as a byte array.
* <p>
- * Type: Bitmap
+ * Type: byte[]
*/
public static final String REQUEST_KEY_ICON = "android.request.icon";
@@ -403,7 +420,7 @@
/**
* Called by an application to check if there is an active Restrictions Provider. If
- * there isn't, {@link #requestPermission(String, Bundle)} is not available.
+ * there isn't, {@link #requestPermission(String, String, PersistableBundle)} is not available.
*
* @return whether there is an active Restrictions Provider.
*/
@@ -428,40 +445,54 @@
* Restrictions Provider might understand. For custom types, the type name should be
* namespaced to avoid collisions with predefined types and types specified by
* other Restrictions Providers.
- * @param request A Bundle containing the data corresponding to the specified request
+ * @param requestId A unique id generated by the app that contains sufficient information
+ * to identify the parameters of the request when it receives the id in the response.
+ * @param request A PersistableBundle containing the data corresponding to the specified request
* type. The keys for the data in the bundle depend on the request type.
*
* @throws IllegalArgumentException if any of the required parameters are missing.
*/
- public void requestPermission(String requestType, Bundle request) {
+ public void requestPermission(String requestType, String requestId, PersistableBundle request) {
if (requestType == null) {
throw new NullPointerException("requestType cannot be null");
}
+ if (requestId == null) {
+ throw new NullPointerException("requestId cannot be null");
+ }
if (request == null) {
throw new NullPointerException("request cannot be null");
}
- if (!request.containsKey(REQUEST_KEY_ID)) {
- throw new IllegalArgumentException("REQUEST_KEY_ID must be specified");
- }
try {
if (mService != null) {
- mService.requestPermission(mContext.getPackageName(), requestType, request);
+ mService.requestPermission(mContext.getPackageName(), requestType, requestId,
+ request);
}
} catch (RemoteException re) {
Log.w(TAG, "Couldn't reach service");
}
}
+ public Intent getLocalApprovalIntent() {
+ try {
+ if (mService != null) {
+ return mService.getLocalApprovalIntent();
+ }
+ } catch (RemoteException re) {
+ Log.w(TAG, "Couldn't reach service");
+ }
+ return null;
+ }
+
/**
* Called by the Restrictions Provider to deliver a response to an application.
*
* @param packageName the application to deliver the response to. Cannot be null.
- * @param response the Bundle containing the response status, request ID and other information.
+ * @param response the bundle containing the response status, request ID and other information.
* Cannot be null.
*
* @throws IllegalArgumentException if any of the required parameters are missing.
*/
- public void notifyPermissionResponse(String packageName, Bundle response) {
+ public void notifyPermissionResponse(String packageName, PersistableBundle response) {
if (packageName == null) {
throw new NullPointerException("packageName cannot be null");
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index bcf1e87..dbf49c5 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -112,7 +112,7 @@
* Constant corresponding to <code>doNotPersist</code> in
* the {@link android.R.attr#persistableMode} attribute.
*/
- public static final int DO_NOT_PERSIST = 1;
+ public static final int PERSIST_NEVER = 1;
/**
* Constant corresponding to <code>persistAcrossReboots</code> in
* the {@link android.R.attr#persistableMode} attribute.
@@ -667,7 +667,7 @@
private String persistableModeToString() {
switch(persistableMode) {
case PERSIST_ROOT_ONLY: return "PERSIST_ROOT_ONLY";
- case DO_NOT_PERSIST: return "DO_NOT_PERSIST";
+ case PERSIST_NEVER: return "PERSIST_NEVER";
case PERSIST_ACROSS_REBOOTS: return "PERSIST_ACROSS_REBOOTS";
default: return "UNKNOWN=" + persistableMode;
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 95bd480..482ad6e 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -334,11 +334,11 @@
public static final int FLAG_FULL_BACKUP_ONLY = 1<<26;
/**
- * Value for {@link #flags}: true if the application is blocked via restrictions and for
+ * Value for {@link #flags}: true if the application is hidden via restrictions and for
* most purposes is considered as not installed.
* {@hide}
*/
- public static final int FLAG_BLOCKED = 1<<27;
+ public static final int FLAG_HIDDEN = 1<<27;
/**
* Value for {@link #flags}: set to <code>true</code> if the application
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index eb46cf0..4b339a1 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -110,6 +110,8 @@
int getFlagsForUid(int uid);
+ String[] getAppOpPermissionPackages(String permissionName);
+
ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId);
boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId);
@@ -428,8 +430,8 @@
/** Reflects current DeviceStorageMonitorService state */
boolean isStorageLow();
- boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked, int userId);
- boolean getApplicationBlockedSettingAsUser(String packageName, int userId);
+ boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, int userId);
+ boolean getApplicationHiddenSettingAsUser(String packageName, int userId);
IPackageInstaller getPackageInstaller();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ee7810d..8b6ae41 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1142,6 +1142,15 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports the OpenGL ES
+ * <a href="http://www.khronos.org/registry/gles/extensions/ANDROID/ANDROID_extension_pack_es31a.txt">
+ * Android Extension Pack</a>.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device includes an accelerometer.
*/
@SdkConstant(SdkConstantType.FEATURE)
@@ -3633,20 +3642,21 @@
public abstract int getApplicationEnabledSetting(String packageName);
/**
- * Puts the package in a blocked state, which is almost like an uninstalled state,
+ * Puts the package in a hidden state, which is almost like an uninstalled state,
* making the package unavailable, but it doesn't remove the data or the actual
- * package file.
+ * package file. Application can be unhidden by either resetting the hidden state
+ * or by installing it, such as with {@link #installExistingPackage(String)}
* @hide
*/
- public abstract boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked,
+ public abstract boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
UserHandle userHandle);
/**
- * Returns the blocked state of a package.
- * @see #setApplicationBlockedSettingAsUser(String, boolean, UserHandle)
+ * Returns the hidden state of a package.
+ * @see #setApplicationHiddenSettingAsUser(String, boolean, UserHandle)
* @hide
*/
- public abstract boolean getApplicationBlockedSettingAsUser(String packageName,
+ public abstract boolean getApplicationHiddenSettingAsUser(String packageName,
UserHandle userHandle);
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 833dc21..db87cf7 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -382,23 +382,23 @@
}
/**
- * Returns true if the package is installed and not blocked, or if the caller
- * explicitly wanted all uninstalled and blocked packages as well.
+ * Returns true if the package is installed and not hidden, or if the caller
+ * explicitly wanted all uninstalled and hidden packages as well.
*/
- private static boolean checkUseInstalledOrBlocked(int flags, PackageUserState state) {
- return (state.installed && !state.blocked)
+ private static boolean checkUseInstalledOrHidden(int flags, PackageUserState state) {
+ return (state.installed && !state.hidden)
|| (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
}
public static boolean isAvailable(PackageUserState state) {
- return checkUseInstalledOrBlocked(0, state);
+ return checkUseInstalledOrHidden(0, state);
}
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
HashSet<String> grantedPermissions, PackageUserState state, int userId) {
- if (!checkUseInstalledOrBlocked(flags, state)) {
+ if (!checkUseInstalledOrHidden(flags, state)) {
return null;
}
PackageInfo pi = new PackageInfo();
@@ -1530,6 +1530,11 @@
XmlUtils.skipCurrentTag(parser);
+ } else if (tagName.equals("feature-group")) {
+ // Skip this for now until we know what to do with it.
+
+ XmlUtils.skipCurrentTag(parser);
+
} else if (tagName.equals("uses-sdk")) {
if (SDK_VERSION > 0) {
sa = res.obtainAttributes(attrs,
@@ -4533,7 +4538,7 @@
return true;
}
}
- if (!state.installed || state.blocked) {
+ if (!state.installed || state.hidden) {
return true;
}
if (state.stopped) {
@@ -4566,10 +4571,10 @@
} else {
ai.flags &= ~ApplicationInfo.FLAG_INSTALLED;
}
- if (state.blocked) {
- ai.flags |= ApplicationInfo.FLAG_BLOCKED;
+ if (state.hidden) {
+ ai.flags |= ApplicationInfo.FLAG_HIDDEN;
} else {
- ai.flags &= ~ApplicationInfo.FLAG_BLOCKED;
+ ai.flags &= ~ApplicationInfo.FLAG_HIDDEN;
}
if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
@@ -4585,7 +4590,7 @@
public static ApplicationInfo generateApplicationInfo(Package p, int flags,
PackageUserState state, int userId) {
if (p == null) return null;
- if (!checkUseInstalledOrBlocked(flags, state)) {
+ if (!checkUseInstalledOrHidden(flags, state)) {
return null;
}
if (!copyNeeded(flags, p, state, null, userId)
@@ -4673,7 +4678,7 @@
public static final ActivityInfo generateActivityInfo(Activity a, int flags,
PackageUserState state, int userId) {
if (a == null) return null;
- if (!checkUseInstalledOrBlocked(flags, state)) {
+ if (!checkUseInstalledOrHidden(flags, state)) {
return null;
}
if (!copyNeeded(flags, a.owner, state, a.metaData, userId)) {
@@ -4714,7 +4719,7 @@
public static final ServiceInfo generateServiceInfo(Service s, int flags,
PackageUserState state, int userId) {
if (s == null) return null;
- if (!checkUseInstalledOrBlocked(flags, state)) {
+ if (!checkUseInstalledOrHidden(flags, state)) {
return null;
}
if (!copyNeeded(flags, s.owner, state, s.metaData, userId)) {
@@ -4763,7 +4768,7 @@
public static final ProviderInfo generateProviderInfo(Provider p, int flags,
PackageUserState state, int userId) {
if (p == null) return null;
- if (!checkUseInstalledOrBlocked(flags, state)) {
+ if (!checkUseInstalledOrHidden(flags, state)) {
return null;
}
if (!copyNeeded(flags, p.owner, state, p.metaData, userId)
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index f0d4cb1..4dcad6f 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -28,7 +28,7 @@
public boolean stopped;
public boolean notLaunched;
public boolean installed;
- public boolean blocked; // Is the app restricted by owner / admin
+ public boolean hidden; // Is the app restricted by owner / admin
public int enabled;
public boolean blockUninstall;
@@ -39,7 +39,7 @@
public PackageUserState() {
installed = true;
- blocked = false;
+ hidden = false;
enabled = COMPONENT_ENABLED_STATE_DEFAULT;
}
@@ -48,7 +48,7 @@
stopped = o.stopped;
notLaunched = o.notLaunched;
enabled = o.enabled;
- blocked = o.blocked;
+ hidden = o.hidden;
lastDisableAppCaller = o.lastDisableAppCaller;
disabledComponents = o.disabledComponents != null
? new HashSet<String>(o.disabledComponents) : null;
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 5a63e5f..af574db 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -69,6 +69,13 @@
public static final int PROTECTION_FLAG_DEVELOPMENT = 0x20;
/**
+ * Additional flag for {@link #protectionLevel}, corresponding
+ * to the <code>development</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ */
+ public static final int PROTECTION_FLAG_APPOP = 0x40;
+
+ /**
* Mask for {@link #protectionLevel}: the basic protection type.
*/
public static final int PROTECTION_MASK_BASE = 0xf;
@@ -153,6 +160,9 @@
if ((level&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
protLevel += "|development";
}
+ if ((level&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+ protLevel += "|appop";
+ }
return protLevel;
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 38ddede..5face69 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1824,15 +1824,7 @@
remainder = languageTag.substring(separator);
}
- if ("id".equals(language)) {
- return "in" + remainder;
- } else if ("yi".equals(language)) {
- return "ji" + remainder;
- } else if ("he".equals(language)) {
- return "iw" + remainder;
- } else {
- return languageTag;
- }
+ return Locale.adjustLanguageCode(language) + remainder;
}
/**
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index a88e659..6cb6a24 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -16,8 +16,9 @@
package android.hardware.camera2;
-import android.hardware.camera2.CaptureResult.Key;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.PublicKey;
+import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.utils.TypeReference;
import android.util.Rational;
@@ -321,6 +322,7 @@
* @see CaptureRequest#COLOR_CORRECTION_ABERRATION_CORRECTION_MODE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
*/
+ @PublicKey
public static final Key<int[]> COLOR_CORRECTION_AVAILABLE_ABERRATION_CORRECTION_MODES =
new Key<int[]>("android.colorCorrection.availableAberrationCorrectionModes", int[].class);
@@ -332,6 +334,7 @@
* valid anti-banding modes that the application may request
* for this camera device; they must include AUTO.</p>
*/
+ @PublicKey
public static final Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES =
new Key<int[]>("android.control.aeAvailableAntibandingModes", int[].class);
@@ -351,6 +354,7 @@
*
* @see CaptureRequest#CONTROL_AE_MODE
*/
+ @PublicKey
public static final Key<int[]> CONTROL_AE_AVAILABLE_MODES =
new Key<int[]>("android.control.aeAvailableModes", int[].class);
@@ -358,6 +362,7 @@
* <p>List of frame rate ranges supported by the
* auto-exposure (AE) algorithm/hardware</p>
*/
+ @PublicKey
public static final Key<android.util.Range<Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES =
new Key<android.util.Range<Integer>[]>("android.control.aeAvailableTargetFpsRanges", new TypeReference<android.util.Range<Integer>[]>() {{ }});
@@ -368,6 +373,7 @@
*
* @see CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP
*/
+ @PublicKey
public static final Key<android.util.Range<Integer>> CONTROL_AE_COMPENSATION_RANGE =
new Key<android.util.Range<Integer>>("android.control.aeCompensationRange", new TypeReference<android.util.Range<Integer>>() {{ }});
@@ -375,6 +381,7 @@
* <p>Smallest step by which exposure compensation
* can be changed</p>
*/
+ @PublicKey
public static final Key<Rational> CONTROL_AE_COMPENSATION_STEP =
new Key<Rational>("android.control.aeCompensationStep", Rational.class);
@@ -391,6 +398,7 @@
* @see CaptureRequest#CONTROL_AF_MODE
* @see CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE
*/
+ @PublicKey
public static final Key<int[]> CONTROL_AF_AVAILABLE_MODES =
new Key<int[]>("android.control.afAvailableModes", int[].class);
@@ -410,6 +418,7 @@
* @see CaptureRequest#CONTROL_EFFECT_MODE
* @see CaptureRequest#CONTROL_MODE
*/
+ @PublicKey
public static final Key<int[]> CONTROL_AVAILABLE_EFFECTS =
new Key<int[]>("android.control.availableEffects", int[].class);
@@ -424,6 +433,7 @@
*
* @see CaptureRequest#CONTROL_SCENE_MODE
*/
+ @PublicKey
public static final Key<int[]> CONTROL_AVAILABLE_SCENE_MODES =
new Key<int[]>("android.control.availableSceneModes", int[].class);
@@ -431,6 +441,7 @@
* <p>List of video stabilization modes that can
* be supported</p>
*/
+ @PublicKey
public static final Key<int[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES =
new Key<int[]>("android.control.availableVideoStabilizationModes", int[].class);
@@ -450,6 +461,7 @@
* @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
* @see CaptureRequest#CONTROL_AWB_MODE
*/
+ @PublicKey
public static final Key<int[]> CONTROL_AWB_AVAILABLE_MODES =
new Key<int[]>("android.control.awbAvailableModes", int[].class);
@@ -476,6 +488,8 @@
*
* @see CaptureRequest#CONTROL_AE_REGIONS
*/
+ @PublicKey
+ @SyntheticKey
public static final Key<Integer> CONTROL_MAX_REGIONS_AE =
new Key<Integer>("android.control.maxRegionsAe", int.class);
@@ -487,6 +501,8 @@
*
* @see CaptureRequest#CONTROL_AWB_REGIONS
*/
+ @PublicKey
+ @SyntheticKey
public static final Key<Integer> CONTROL_MAX_REGIONS_AWB =
new Key<Integer>("android.control.maxRegionsAwb", int.class);
@@ -498,6 +514,8 @@
*
* @see CaptureRequest#CONTROL_AF_REGIONS
*/
+ @PublicKey
+ @SyntheticKey
public static final Key<Integer> CONTROL_MAX_REGIONS_AF =
new Key<Integer>("android.control.maxRegionsAf", int.class);
@@ -538,6 +556,7 @@
*
* @see CaptureRequest#EDGE_MODE
*/
+ @PublicKey
public static final Key<int[]> EDGE_AVAILABLE_EDGE_MODES =
new Key<int[]>("android.edge.availableEdgeModes", int[].class);
@@ -547,6 +566,7 @@
* <p>If no flash, none of the flash controls do
* anything. All other metadata should return 0.</p>
*/
+ @PublicKey
public static final Key<Boolean> FLASH_INFO_AVAILABLE =
new Key<Boolean>("android.flash.info.available", boolean.class);
@@ -558,6 +578,7 @@
*
* @see CaptureRequest#HOT_PIXEL_MODE
*/
+ @PublicKey
public static final Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES =
new Key<int[]>("android.hotPixel.availableHotPixelModes", int[].class);
@@ -577,6 +598,7 @@
* <li>All non (0, 0) sizes will have non-zero widths and heights.</li>
* </ul>
*/
+ @PublicKey
public static final Key<android.util.Size[]> JPEG_AVAILABLE_THUMBNAIL_SIZES =
new Key<android.util.Size[]>("android.jpeg.availableThumbnailSizes", android.util.Size[].class);
@@ -588,6 +610,7 @@
* <p>If the camera device supports variable apertures, the aperture value
* in this list will be sorted in ascending order.</p>
*/
+ @PublicKey
public static final Key<float[]> LENS_INFO_AVAILABLE_APERTURES =
new Key<float[]>("android.lens.info.availableApertures", float[].class);
@@ -601,6 +624,7 @@
*
* @see CaptureRequest#LENS_FILTER_DENSITY
*/
+ @PublicKey
public static final Key<float[]> LENS_INFO_AVAILABLE_FILTER_DENSITIES =
new Key<float[]>("android.lens.info.availableFilterDensities", float[].class);
@@ -614,6 +638,7 @@
*
* @see CaptureRequest#LENS_FOCAL_LENGTH
*/
+ @PublicKey
public static final Key<float[]> LENS_INFO_AVAILABLE_FOCAL_LENGTHS =
new Key<float[]>("android.lens.info.availableFocalLengths", float[].class);
@@ -626,6 +651,7 @@
*
* @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
*/
+ @PublicKey
public static final Key<int[]> LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION =
new Key<int[]>("android.lens.info.availableOpticalStabilization", int[].class);
@@ -637,6 +663,7 @@
*
* @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION
*/
+ @PublicKey
public static final Key<Float> LENS_INFO_HYPERFOCAL_DISTANCE =
new Key<Float>("android.lens.info.hyperfocalDistance", float.class);
@@ -647,6 +674,7 @@
* 0.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*/
+ @PublicKey
public static final Key<Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE =
new Key<Float>("android.lens.info.minimumFocusDistance", float.class);
@@ -674,6 +702,7 @@
* @see #LENS_INFO_FOCUS_DISTANCE_CALIBRATION_APPROXIMATE
* @see #LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED
*/
+ @PublicKey
public static final Key<Integer> LENS_INFO_FOCUS_DISTANCE_CALIBRATION =
new Key<Integer>("android.lens.info.focusDistanceCalibration", int.class);
@@ -683,6 +712,7 @@
* @see #LENS_FACING_FRONT
* @see #LENS_FACING_BACK
*/
+ @PublicKey
public static final Key<Integer> LENS_FACING =
new Key<Integer>("android.lens.facing", int.class);
@@ -693,6 +723,7 @@
*
* @see CaptureRequest#NOISE_REDUCTION_MODE
*/
+ @PublicKey
public static final Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES =
new Key<int[]>("android.noiseReduction.availableNoiseReductionModes", int[].class);
@@ -758,6 +789,8 @@
*
* @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
*/
+ @PublicKey
+ @SyntheticKey
public static final Key<Integer> REQUEST_MAX_NUM_OUTPUT_RAW =
new Key<Integer>("android.request.maxNumOutputRaw", int.class);
@@ -784,6 +817,8 @@
*
* @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
*/
+ @PublicKey
+ @SyntheticKey
public static final Key<Integer> REQUEST_MAX_NUM_OUTPUT_PROC =
new Key<Integer>("android.request.maxNumOutputProc", int.class);
@@ -804,6 +839,8 @@
*
* @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
*/
+ @PublicKey
+ @SyntheticKey
public static final Key<Integer> REQUEST_MAX_NUM_OUTPUT_PROC_STALLING =
new Key<Integer>("android.request.maxNumOutputProcStalling", int.class);
@@ -842,6 +879,7 @@
*
* @see CaptureResult#REQUEST_PIPELINE_DEPTH
*/
+ @PublicKey
public static final Key<Byte> REQUEST_PIPELINE_MAX_DEPTH =
new Key<Byte>("android.request.pipelineMaxDepth", byte.class);
@@ -862,6 +900,7 @@
* partial results.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*/
+ @PublicKey
public static final Key<Integer> REQUEST_PARTIAL_RESULT_COUNT =
new Key<Integer>("android.request.partialResultCount", int.class);
@@ -892,6 +931,7 @@
* @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
* @see #REQUEST_AVAILABLE_CAPABILITIES_RAW
*/
+ @PublicKey
public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
new Key<int[]>("android.request.availableCapabilities", int[].class);
@@ -1001,6 +1041,7 @@
* than this ratio allows will be rounded up to the minimum
* allowed size by the camera device.</p>
*/
+ @PublicKey
public static final Key<Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM =
new Key<Float>("android.scaler.availableMaxDigitalZoom", float.class);
@@ -1360,6 +1401,8 @@
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
+ @PublicKey
+ @SyntheticKey
public static final Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP =
new Key<android.hardware.camera2.params.StreamConfigurationMap>("android.scaler.streamConfigurationMap", android.hardware.camera2.params.StreamConfigurationMap.class);
@@ -1382,6 +1425,7 @@
* @see #SCALER_CROPPING_TYPE_CENTER_ONLY
* @see #SCALER_CROPPING_TYPE_FREEFORM
*/
+ @PublicKey
public static final Key<Integer> SCALER_CROPPING_TYPE =
new Key<Integer>("android.scaler.croppingType", int.class);
@@ -1391,6 +1435,7 @@
* <p>It is smaller or equal to
* sensor full pixel array, which could include the black calibration pixels.</p>
*/
+ @PublicKey
public static final Key<android.graphics.Rect> SENSOR_INFO_ACTIVE_ARRAY_SIZE =
new Key<android.graphics.Rect>("android.sensor.info.activeArraySize", android.graphics.Rect.class);
@@ -1403,6 +1448,7 @@
*
* @see CaptureRequest#SENSOR_SENSITIVITY
*/
+ @PublicKey
public static final Key<android.util.Range<Integer>> SENSOR_INFO_SENSITIVITY_RANGE =
new Key<android.util.Range<Integer>>("android.sensor.info.sensitivityRange", new TypeReference<android.util.Range<Integer>>() {{ }});
@@ -1416,6 +1462,7 @@
* @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR
* @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB
*/
+ @PublicKey
public static final Key<Integer> SENSOR_INFO_COLOR_FILTER_ARRANGEMENT =
new Key<Integer>("android.sensor.info.colorFilterArrangement", int.class);
@@ -1429,6 +1476,7 @@
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CaptureRequest#SENSOR_EXPOSURE_TIME
*/
+ @PublicKey
public static final Key<android.util.Range<Long>> SENSOR_INFO_EXPOSURE_TIME_RANGE =
new Key<android.util.Range<Long>>("android.sensor.info.exposureTimeRange", new TypeReference<android.util.Range<Long>>() {{ }});
@@ -1449,6 +1497,7 @@
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CaptureRequest#SENSOR_FRAME_DURATION
*/
+ @PublicKey
public static final Key<Long> SENSOR_INFO_MAX_FRAME_DURATION =
new Key<Long>("android.sensor.info.maxFrameDuration", long.class);
@@ -1460,6 +1509,7 @@
*
* @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
*/
+ @PublicKey
public static final Key<android.util.SizeF> SENSOR_INFO_PHYSICAL_SIZE =
new Key<android.util.SizeF>("android.sensor.info.physicalSize", android.util.SizeF.class);
@@ -1478,6 +1528,7 @@
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE
*/
+ @PublicKey
public static final Key<android.util.Size> SENSOR_INFO_PIXEL_ARRAY_SIZE =
new Key<android.util.Size>("android.sensor.info.pixelArraySize", android.util.Size.class);
@@ -1495,6 +1546,7 @@
*
* @see CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN
*/
+ @PublicKey
public static final Key<Integer> SENSOR_INFO_WHITE_LEVEL =
new Key<Integer>("android.sensor.info.whiteLevel", int.class);
@@ -1507,6 +1559,7 @@
* @see #SENSOR_INFO_TIMESTAMP_CALIBRATION_UNCALIBRATED
* @see #SENSOR_INFO_TIMESTAMP_CALIBRATION_CALIBRATED
*/
+ @PublicKey
public static final Key<Integer> SENSOR_INFO_TIMESTAMP_CALIBRATION =
new Key<Integer>("android.sensor.info.timestampCalibration", int.class);
@@ -1549,6 +1602,7 @@
* @see #SENSOR_REFERENCE_ILLUMINANT1_D50
* @see #SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN
*/
+ @PublicKey
public static final Key<Integer> SENSOR_REFERENCE_ILLUMINANT1 =
new Key<Integer>("android.sensor.referenceIlluminant1", int.class);
@@ -1569,6 +1623,7 @@
* @see CameraCharacteristics#SENSOR_FORWARD_MATRIX2
* @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
*/
+ @PublicKey
public static final Key<Byte> SENSOR_REFERENCE_ILLUMINANT2 =
new Key<Byte>("android.sensor.referenceIlluminant2", byte.class);
@@ -1587,6 +1642,7 @@
*
* @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_CALIBRATION_TRANSFORM1 =
new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.calibrationTransform1", android.hardware.camera2.params.ColorSpaceTransform.class);
@@ -1608,6 +1664,7 @@
*
* @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_CALIBRATION_TRANSFORM2 =
new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.calibrationTransform2", android.hardware.camera2.params.ColorSpaceTransform.class);
@@ -1630,6 +1687,7 @@
*
* @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_COLOR_TRANSFORM1 =
new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.colorTransform1", android.hardware.camera2.params.ColorSpaceTransform.class);
@@ -1654,6 +1712,7 @@
*
* @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_COLOR_TRANSFORM2 =
new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.colorTransform2", android.hardware.camera2.params.ColorSpaceTransform.class);
@@ -1674,6 +1733,7 @@
*
* @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_FORWARD_MATRIX1 =
new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.forwardMatrix1", android.hardware.camera2.params.ColorSpaceTransform.class);
@@ -1696,6 +1756,7 @@
*
* @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_FORWARD_MATRIX2 =
new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.forwardMatrix2", android.hardware.camera2.params.ColorSpaceTransform.class);
@@ -1714,6 +1775,7 @@
* @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
* @see CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.BlackLevelPattern> SENSOR_BLACK_LEVEL_PATTERN =
new Key<android.hardware.camera2.params.BlackLevelPattern>("android.sensor.blackLevelPattern", android.hardware.camera2.params.BlackLevelPattern.class);
@@ -1732,6 +1794,7 @@
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CaptureRequest#SENSOR_SENSITIVITY
*/
+ @PublicKey
public static final Key<Integer> SENSOR_MAX_ANALOG_SENSITIVITY =
new Key<Integer>("android.sensor.maxAnalogSensitivity", int.class);
@@ -1742,42 +1805,18 @@
* direction of rolling shutter readout, which is from top
* to bottom in the sensor's coordinate system</p>
*/
+ @PublicKey
public static final Key<Integer> SENSOR_ORIENTATION =
new Key<Integer>("android.sensor.orientation", int.class);
/**
- * <p>Noise model coefficients for each CFA mosaic channel.</p>
- * <p>This tag contains two noise model coefficients for each CFA channel
- * corresponding to the sensor amplification (S) and sensor readout
- * noise (O). These are given as pairs of coefficients for each channel
- * in the same order as channels listed for the CFA layout tag
- * (see {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT android.sensor.info.colorFilterArrangement}). This is
- * represented as an array of Pair<Double, Double>, where
- * the first member of the Pair at index n is the S coefficient and the
- * second member is the O coefficient for the nth color channel in the CFA.</p>
- * <p>These coefficients are used in a two parameter noise model to describe
- * the amount of noise present in the image for each CFA channel. The
- * noise model used here is:</p>
- * <p>N(x) = sqrt(Sx + O)</p>
- * <p>Where x represents the recorded signal of a CFA channel normalized to
- * the range [0, 1], and S and O are the noise model coeffiecients for
- * that channel.</p>
- * <p>A more detailed description of the noise model can be found in the
- * Adobe DNG specification for the NoiseProfile tag.</p>
- * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
- *
- * @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
- */
- public static final Key<android.util.Pair<Double,Double>[]> SENSOR_NOISE_PROFILE =
- new Key<android.util.Pair<Double,Double>[]>("android.sensor.noiseProfile", new TypeReference<android.util.Pair<Double,Double>[]>() {{ }});
-
- /**
* <p>Lists the supported sensor test pattern modes for {@link CaptureRequest#SENSOR_TEST_PATTERN_MODE android.sensor.testPatternMode}.</p>
* <p>Optional. Defaults to [OFF].</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CaptureRequest#SENSOR_TEST_PATTERN_MODE
*/
+ @PublicKey
public static final Key<int[]> SENSOR_AVAILABLE_TEST_PATTERN_MODES =
new Key<int[]>("android.sensor.availableTestPatternModes", int[].class);
@@ -1792,6 +1831,7 @@
* android.statistics.faceIds and
* android.statistics.faceLandmarks outputs.</p>
*/
+ @PublicKey
public static final Key<int[]> STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES =
new Key<int[]>("android.statistics.info.availableFaceDetectModes", int[].class);
@@ -1799,6 +1839,7 @@
* <p>The maximum number of simultaneously detectable
* faces.</p>
*/
+ @PublicKey
public static final Key<Integer> STATISTICS_INFO_MAX_FACE_COUNT =
new Key<Integer>("android.statistics.info.maxFaceCount", int.class);
@@ -1811,6 +1852,7 @@
*
* @see CaptureRequest#STATISTICS_HOT_PIXEL_MAP_MODE
*/
+ @PublicKey
public static final Key<boolean[]> STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES =
new Key<boolean[]>("android.statistics.info.availableHotPixelMapModes", boolean[].class);
@@ -1827,6 +1869,7 @@
*
* @see CaptureRequest#TONEMAP_CURVE
*/
+ @PublicKey
public static final Key<Integer> TONEMAP_MAX_CURVE_POINTS =
new Key<Integer>("android.tonemap.maxCurvePoints", int.class);
@@ -1838,6 +1881,7 @@
*
* @see CaptureRequest#TONEMAP_MODE
*/
+ @PublicKey
public static final Key<int[]> TONEMAP_AVAILABLE_TONE_MAP_MODES =
new Key<int[]>("android.tonemap.availableToneMapModes", int[].class);
@@ -1879,6 +1923,7 @@
* @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
* @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
*/
+ @PublicKey
public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL =
new Key<Integer>("android.info.supportedHardwareLevel", int.class);
@@ -1917,10 +1962,12 @@
* @see #SYNC_MAX_LATENCY_PER_FRAME_CONTROL
* @see #SYNC_MAX_LATENCY_UNKNOWN
*/
+ @PublicKey
public static final Key<Integer> SYNC_MAX_LATENCY =
new Key<Integer>("android.sync.maxLatency", int.class);
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
+
}
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 63130a7..ebbfc63 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -17,6 +17,8 @@
package android.hardware.camera2;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.PublicKey;
+import android.hardware.camera2.impl.SyntheticKey;
import android.util.Log;
import java.lang.reflect.Field;
@@ -151,7 +153,7 @@
}
if (instance == null || instance.getProtected(key) != null) {
- if (shouldKeyBeAdded(key, filterTags)) {
+ if (shouldKeyBeAdded(key, field, filterTags)) {
keyList.add(key);
if (VERBOSE) {
@@ -168,7 +170,7 @@
}
@SuppressWarnings("rawtypes")
- private static <TKey> boolean shouldKeyBeAdded(TKey key, int[] filterTags) {
+ private static <TKey> boolean shouldKeyBeAdded(TKey key, Field field, int[] filterTags) {
if (key == null) {
throw new NullPointerException("key must not be null");
}
@@ -189,11 +191,27 @@
throw new IllegalArgumentException("key type must be that of a metadata key");
}
+ if (field.getAnnotation(PublicKey.class) == null) {
+ // Never expose @hide keys up to the API user
+ return false;
+ }
+
// No filtering necessary
if (filterTags == null) {
return true;
}
+ if (field.getAnnotation(SyntheticKey.class) != null) {
+ // This key is synthetic, so calling #getTag will throw IAE
+
+ // TODO: don't just assume all public+synthetic keys are always available
+ return true;
+ }
+
+ /*
+ * Regular key: look up it's native tag and see if it's in filterTags
+ */
+
int keyTag = nativeKey.getTag();
// non-negative result is returned iff the value is in the array
@@ -1189,7 +1207,8 @@
* image while recording video) use case.</p>
* <p>The camera device should take the highest-quality image
* possible (given the other settings) without disrupting the
- * frame rate of video recording. </p>
+ * frame rate of video recording.<br />
+ * </p>
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
*/
public static final int CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT = 4;
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 964f4f3..97077e6 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -16,13 +16,13 @@
package android.hardware.camera2;
-import android.hardware.camera2.CameraCharacteristics.Key;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.PublicKey;
+import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.hardware.camera2.utils.TypeReference;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.Rational;
import android.view.Surface;
import java.util.Collection;
@@ -526,6 +526,7 @@
* @see #COLOR_CORRECTION_MODE_FAST
* @see #COLOR_CORRECTION_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> COLOR_CORRECTION_MODE =
new Key<Integer>("android.colorCorrection.mode", int.class);
@@ -544,6 +545,7 @@
*
* @see CaptureRequest#COLOR_CORRECTION_MODE
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.ColorSpaceTransform> COLOR_CORRECTION_TRANSFORM =
new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.colorCorrection.transform", android.hardware.camera2.params.ColorSpaceTransform.class);
@@ -560,6 +562,7 @@
*
* @see CaptureRequest#COLOR_CORRECTION_MODE
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.RggbChannelVector> COLOR_CORRECTION_GAINS =
new Key<android.hardware.camera2.params.RggbChannelVector>("android.colorCorrection.gains", android.hardware.camera2.params.RggbChannelVector.class);
@@ -583,6 +586,7 @@
* @see #COLOR_CORRECTION_ABERRATION_CORRECTION_MODE_FAST
* @see #COLOR_CORRECTION_ABERRATION_CORRECTION_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> COLOR_CORRECTION_ABERRATION_CORRECTION_MODE =
new Key<Integer>("android.colorCorrection.aberrationCorrectionMode", int.class);
@@ -626,6 +630,7 @@
* @see #CONTROL_AE_ANTIBANDING_MODE_60HZ
* @see #CONTROL_AE_ANTIBANDING_MODE_AUTO
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AE_ANTIBANDING_MODE =
new Key<Integer>("android.control.aeAntibandingMode", int.class);
@@ -654,6 +659,7 @@
* @see CaptureRequest#CONTROL_AE_MODE
* @see CaptureResult#CONTROL_AE_STATE
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AE_EXPOSURE_COMPENSATION =
new Key<Integer>("android.control.aeExposureCompensation", int.class);
@@ -680,6 +686,7 @@
* @see CaptureRequest#SENSOR_EXPOSURE_TIME
* @see CaptureRequest#SENSOR_SENSITIVITY
*/
+ @PublicKey
public static final Key<Boolean> CONTROL_AE_LOCK =
new Key<Boolean>("android.control.aeLock", boolean.class);
@@ -717,6 +724,7 @@
* @see #CONTROL_AE_MODE_ON_ALWAYS_FLASH
* @see #CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AE_MODE =
new Key<Integer>("android.control.aeMode", int.class);
@@ -742,6 +750,7 @@
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS =
new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.aeRegions", android.hardware.camera2.params.MeteringRectangle[].class);
@@ -753,6 +762,7 @@
*
* @see CaptureRequest#SENSOR_EXPOSURE_TIME
*/
+ @PublicKey
public static final Key<android.util.Range<Integer>> CONTROL_AE_TARGET_FPS_RANGE =
new Key<android.util.Range<Integer>>("android.control.aeTargetFpsRange", new TypeReference<android.util.Range<Integer>>() {{ }});
@@ -780,6 +790,7 @@
* @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE
* @see #CONTROL_AE_PRECAPTURE_TRIGGER_START
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AE_PRECAPTURE_TRIGGER =
new Key<Integer>("android.control.aePrecaptureTrigger", int.class);
@@ -802,6 +813,7 @@
* @see #CONTROL_AF_MODE_CONTINUOUS_PICTURE
* @see #CONTROL_AF_MODE_EDOF
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AF_MODE =
new Key<Integer>("android.control.afMode", int.class);
@@ -827,6 +839,7 @@
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS =
new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.afRegions", android.hardware.camera2.params.MeteringRectangle[].class);
@@ -849,6 +862,7 @@
* @see #CONTROL_AF_TRIGGER_START
* @see #CONTROL_AF_TRIGGER_CANCEL
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AF_TRIGGER =
new Key<Integer>("android.control.afTrigger", int.class);
@@ -861,6 +875,7 @@
*
* @see CaptureRequest#CONTROL_AWB_MODE
*/
+ @PublicKey
public static final Key<Boolean> CONTROL_AWB_LOCK =
new Key<Boolean>("android.control.awbLock", boolean.class);
@@ -899,6 +914,7 @@
* @see #CONTROL_AWB_MODE_TWILIGHT
* @see #CONTROL_AWB_MODE_SHADE
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AWB_MODE =
new Key<Integer>("android.control.awbMode", int.class);
@@ -924,6 +940,7 @@
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS =
new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.awbRegions", android.hardware.camera2.params.MeteringRectangle[].class);
@@ -948,6 +965,7 @@
* @see #CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG
* @see #CONTROL_CAPTURE_INTENT_MANUAL
*/
+ @PublicKey
public static final Key<Integer> CONTROL_CAPTURE_INTENT =
new Key<Integer>("android.control.captureIntent", int.class);
@@ -973,6 +991,7 @@
* @see #CONTROL_EFFECT_MODE_BLACKBOARD
* @see #CONTROL_EFFECT_MODE_AQUA
*/
+ @PublicKey
public static final Key<Integer> CONTROL_EFFECT_MODE =
new Key<Integer>("android.control.effectMode", int.class);
@@ -1001,6 +1020,7 @@
* @see #CONTROL_MODE_USE_SCENE_MODE
* @see #CONTROL_MODE_OFF_KEEP_STATE
*/
+ @PublicKey
public static final Key<Integer> CONTROL_MODE =
new Key<Integer>("android.control.mode", int.class);
@@ -1042,6 +1062,7 @@
* @see #CONTROL_SCENE_MODE_BARCODE
* @see #CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO
*/
+ @PublicKey
public static final Key<Integer> CONTROL_SCENE_MODE =
new Key<Integer>("android.control.sceneMode", int.class);
@@ -1066,6 +1087,7 @@
* @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF
* @see #CONTROL_VIDEO_STABILIZATION_MODE_ON
*/
+ @PublicKey
public static final Key<Integer> CONTROL_VIDEO_STABILIZATION_MODE =
new Key<Integer>("android.control.videoStabilizationMode", int.class);
@@ -1086,6 +1108,7 @@
* @see #EDGE_MODE_FAST
* @see #EDGE_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> EDGE_MODE =
new Key<Integer>("android.edge.mode", int.class);
@@ -1113,6 +1136,7 @@
* @see #FLASH_MODE_SINGLE
* @see #FLASH_MODE_TORCH
*/
+ @PublicKey
public static final Key<Integer> FLASH_MODE =
new Key<Integer>("android.flash.mode", int.class);
@@ -1129,12 +1153,15 @@
* @see #HOT_PIXEL_MODE_FAST
* @see #HOT_PIXEL_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> HOT_PIXEL_MODE =
new Key<Integer>("android.hotPixel.mode", int.class);
/**
* <p>A location object to use when generating image GPS metadata.</p>
*/
+ @PublicKey
+ @SyntheticKey
public static final Key<android.location.Location> JPEG_GPS_LOCATION =
new Key<android.location.Location>("android.jpeg.gpsLocation", android.location.Location.class);
@@ -1166,6 +1193,7 @@
* <p>Orientation of JPEG image to
* write</p>
*/
+ @PublicKey
public static final Key<Integer> JPEG_ORIENTATION =
new Key<Integer>("android.jpeg.orientation", int.class);
@@ -1174,6 +1202,7 @@
* image.</p>
* <p>85-95 is typical usage range.</p>
*/
+ @PublicKey
public static final Key<Byte> JPEG_QUALITY =
new Key<Byte>("android.jpeg.quality", byte.class);
@@ -1181,6 +1210,7 @@
* <p>Compression quality of JPEG
* thumbnail.</p>
*/
+ @PublicKey
public static final Key<Byte> JPEG_THUMBNAIL_QUALITY =
new Key<Byte>("android.jpeg.thumbnailQuality", byte.class);
@@ -1197,6 +1227,7 @@
* generate the thumbnail image. The thumbnail image will always have a smaller Field
* Of View (FOV) than the primary image when aspect ratios differ.</p>
*/
+ @PublicKey
public static final Key<android.util.Size> JPEG_THUMBNAIL_SIZE =
new Key<android.util.Size>("android.jpeg.thumbnailSize", android.util.Size.class);
@@ -1226,6 +1257,7 @@
* @see CaptureRequest#SENSOR_FRAME_DURATION
* @see CaptureRequest#SENSOR_SENSITIVITY
*/
+ @PublicKey
public static final Key<Float> LENS_APERTURE =
new Key<Float>("android.lens.aperture", float.class);
@@ -1249,6 +1281,7 @@
* @see CameraCharacteristics#LENS_INFO_AVAILABLE_FILTER_DENSITIES
* @see CaptureResult#LENS_STATE
*/
+ @PublicKey
public static final Key<Float> LENS_FILTER_DENSITY =
new Key<Float>("android.lens.filterDensity", float.class);
@@ -1268,6 +1301,7 @@
* @see CaptureRequest#LENS_FOCUS_DISTANCE
* @see CaptureResult#LENS_STATE
*/
+ @PublicKey
public static final Key<Float> LENS_FOCAL_LENGTH =
new Key<Float>("android.lens.focalLength", float.class);
@@ -1285,6 +1319,7 @@
* @see CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE
* @see CaptureResult#LENS_STATE
*/
+ @PublicKey
public static final Key<Float> LENS_FOCUS_DISTANCE =
new Key<Float>("android.lens.focusDistance", float.class);
@@ -1314,6 +1349,7 @@
* @see #LENS_OPTICAL_STABILIZATION_MODE_OFF
* @see #LENS_OPTICAL_STABILIZATION_MODE_ON
*/
+ @PublicKey
public static final Key<Integer> LENS_OPTICAL_STABILIZATION_MODE =
new Key<Integer>("android.lens.opticalStabilizationMode", int.class);
@@ -1334,6 +1370,7 @@
* @see #NOISE_REDUCTION_MODE_FAST
* @see #NOISE_REDUCTION_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> NOISE_REDUCTION_MODE =
new Key<Integer>("android.noiseReduction.mode", int.class);
@@ -1381,6 +1418,7 @@
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
+ @PublicKey
public static final Key<android.graphics.Rect> SCALER_CROP_REGION =
new Key<android.graphics.Rect>("android.scaler.cropRegion", android.graphics.Rect.class);
@@ -1390,6 +1428,7 @@
* <p>If the sensor can't expose this exact duration, it should shorten the
* duration exposed to the nearest possible value (rather than expose longer).</p>
*/
+ @PublicKey
public static final Key<Long> SENSOR_EXPOSURE_TIME =
new Key<Long>("android.sensor.exposureTime", long.class);
@@ -1468,6 +1507,7 @@
*
* @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
*/
+ @PublicKey
public static final Key<Long> SENSOR_FRAME_DURATION =
new Key<Long>("android.sensor.frameDuration", long.class);
@@ -1487,6 +1527,7 @@
* @see CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE
* @see CameraCharacteristics#SENSOR_MAX_ANALOG_SENSITIVITY
*/
+ @PublicKey
public static final Key<Integer> SENSOR_SENSITIVITY =
new Key<Integer>("android.sensor.sensitivity", int.class);
@@ -1503,6 +1544,7 @@
*
* @see CaptureRequest#SENSOR_TEST_PATTERN_MODE
*/
+ @PublicKey
public static final Key<int[]> SENSOR_TEST_PATTERN_DATA =
new Key<int[]>("android.sensor.testPatternData", int[].class);
@@ -1523,6 +1565,7 @@
* @see #SENSOR_TEST_PATTERN_MODE_PN9
* @see #SENSOR_TEST_PATTERN_MODE_CUSTOM1
*/
+ @PublicKey
public static final Key<Integer> SENSOR_TEST_PATTERN_MODE =
new Key<Integer>("android.sensor.testPatternMode", int.class);
@@ -1561,6 +1604,7 @@
* @see #SHADING_MODE_FAST
* @see #SHADING_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> SHADING_MODE =
new Key<Integer>("android.shading.mode", int.class);
@@ -1577,6 +1621,7 @@
* @see #STATISTICS_FACE_DETECT_MODE_SIMPLE
* @see #STATISTICS_FACE_DETECT_MODE_FULL
*/
+ @PublicKey
public static final Key<Integer> STATISTICS_FACE_DETECT_MODE =
new Key<Integer>("android.statistics.faceDetectMode", int.class);
@@ -1589,6 +1634,7 @@
* @see CaptureResult#STATISTICS_HOT_PIXEL_MAP
* @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES
*/
+ @PublicKey
public static final Key<Boolean> STATISTICS_HOT_PIXEL_MAP_MODE =
new Key<Boolean>("android.statistics.hotPixelMapMode", boolean.class);
@@ -1601,6 +1647,7 @@
* @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF
* @see #STATISTICS_LENS_SHADING_MAP_MODE_ON
*/
+ @PublicKey
public static final Key<Integer> STATISTICS_LENS_SHADING_MAP_MODE =
new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
@@ -1730,6 +1777,8 @@
* @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
* @see CaptureRequest#TONEMAP_MODE
*/
+ @PublicKey
+ @SyntheticKey
public static final Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE =
new Key<android.hardware.camera2.params.TonemapCurve>("android.tonemap.curve", android.hardware.camera2.params.TonemapCurve.class);
@@ -1763,6 +1812,7 @@
* @see #TONEMAP_MODE_FAST
* @see #TONEMAP_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> TONEMAP_MODE =
new Key<Integer>("android.tonemap.mode", int.class);
@@ -1821,10 +1871,12 @@
* other request parameters require a black level recalculation
* or reset.</p>
*/
+ @PublicKey
public static final Key<Boolean> BLACK_LEVEL_LOCK =
new Key<Boolean>("android.blackLevel.lock", boolean.class);
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
+
}
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 956e64f..20a04f0 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -17,6 +17,8 @@
package android.hardware.camera2;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.PublicKey;
+import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.utils.TypeReference;
import android.util.Log;
import android.util.Rational;
@@ -365,6 +367,7 @@
* @see #COLOR_CORRECTION_MODE_FAST
* @see #COLOR_CORRECTION_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> COLOR_CORRECTION_MODE =
new Key<Integer>("android.colorCorrection.mode", int.class);
@@ -383,6 +386,7 @@
*
* @see CaptureRequest#COLOR_CORRECTION_MODE
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.ColorSpaceTransform> COLOR_CORRECTION_TRANSFORM =
new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.colorCorrection.transform", android.hardware.camera2.params.ColorSpaceTransform.class);
@@ -399,6 +403,7 @@
*
* @see CaptureRequest#COLOR_CORRECTION_MODE
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.RggbChannelVector> COLOR_CORRECTION_GAINS =
new Key<android.hardware.camera2.params.RggbChannelVector>("android.colorCorrection.gains", android.hardware.camera2.params.RggbChannelVector.class);
@@ -422,6 +427,7 @@
* @see #COLOR_CORRECTION_ABERRATION_CORRECTION_MODE_FAST
* @see #COLOR_CORRECTION_ABERRATION_CORRECTION_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> COLOR_CORRECTION_ABERRATION_CORRECTION_MODE =
new Key<Integer>("android.colorCorrection.aberrationCorrectionMode", int.class);
@@ -465,6 +471,7 @@
* @see #CONTROL_AE_ANTIBANDING_MODE_60HZ
* @see #CONTROL_AE_ANTIBANDING_MODE_AUTO
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AE_ANTIBANDING_MODE =
new Key<Integer>("android.control.aeAntibandingMode", int.class);
@@ -493,6 +500,7 @@
* @see CaptureRequest#CONTROL_AE_MODE
* @see CaptureResult#CONTROL_AE_STATE
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AE_EXPOSURE_COMPENSATION =
new Key<Integer>("android.control.aeExposureCompensation", int.class);
@@ -519,6 +527,7 @@
* @see CaptureRequest#SENSOR_EXPOSURE_TIME
* @see CaptureRequest#SENSOR_SENSITIVITY
*/
+ @PublicKey
public static final Key<Boolean> CONTROL_AE_LOCK =
new Key<Boolean>("android.control.aeLock", boolean.class);
@@ -556,6 +565,7 @@
* @see #CONTROL_AE_MODE_ON_ALWAYS_FLASH
* @see #CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AE_MODE =
new Key<Integer>("android.control.aeMode", int.class);
@@ -581,6 +591,7 @@
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS =
new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.aeRegions", android.hardware.camera2.params.MeteringRectangle[].class);
@@ -592,6 +603,7 @@
*
* @see CaptureRequest#SENSOR_EXPOSURE_TIME
*/
+ @PublicKey
public static final Key<android.util.Range<Integer>> CONTROL_AE_TARGET_FPS_RANGE =
new Key<android.util.Range<Integer>>("android.control.aeTargetFpsRange", new TypeReference<android.util.Range<Integer>>() {{ }});
@@ -619,6 +631,7 @@
* @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE
* @see #CONTROL_AE_PRECAPTURE_TRIGGER_START
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AE_PRECAPTURE_TRIGGER =
new Key<Integer>("android.control.aePrecaptureTrigger", int.class);
@@ -817,6 +830,7 @@
* @see #CONTROL_AE_STATE_FLASH_REQUIRED
* @see #CONTROL_AE_STATE_PRECAPTURE
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AE_STATE =
new Key<Integer>("android.control.aeState", int.class);
@@ -839,6 +853,7 @@
* @see #CONTROL_AF_MODE_CONTINUOUS_PICTURE
* @see #CONTROL_AF_MODE_EDOF
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AF_MODE =
new Key<Integer>("android.control.afMode", int.class);
@@ -864,6 +879,7 @@
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS =
new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.afRegions", android.hardware.camera2.params.MeteringRectangle[].class);
@@ -886,6 +902,7 @@
* @see #CONTROL_AF_TRIGGER_START
* @see #CONTROL_AF_TRIGGER_CANCEL
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AF_TRIGGER =
new Key<Integer>("android.control.afTrigger", int.class);
@@ -1283,6 +1300,7 @@
* @see #CONTROL_AF_STATE_NOT_FOCUSED_LOCKED
* @see #CONTROL_AF_STATE_PASSIVE_UNFOCUSED
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AF_STATE =
new Key<Integer>("android.control.afState", int.class);
@@ -1295,6 +1313,7 @@
*
* @see CaptureRequest#CONTROL_AWB_MODE
*/
+ @PublicKey
public static final Key<Boolean> CONTROL_AWB_LOCK =
new Key<Boolean>("android.control.awbLock", boolean.class);
@@ -1333,6 +1352,7 @@
* @see #CONTROL_AWB_MODE_TWILIGHT
* @see #CONTROL_AWB_MODE_SHADE
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AWB_MODE =
new Key<Integer>("android.control.awbMode", int.class);
@@ -1358,6 +1378,7 @@
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS =
new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.awbRegions", android.hardware.camera2.params.MeteringRectangle[].class);
@@ -1382,6 +1403,7 @@
* @see #CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG
* @see #CONTROL_CAPTURE_INTENT_MANUAL
*/
+ @PublicKey
public static final Key<Integer> CONTROL_CAPTURE_INTENT =
new Key<Integer>("android.control.captureIntent", int.class);
@@ -1512,6 +1534,7 @@
* @see #CONTROL_AWB_STATE_CONVERGED
* @see #CONTROL_AWB_STATE_LOCKED
*/
+ @PublicKey
public static final Key<Integer> CONTROL_AWB_STATE =
new Key<Integer>("android.control.awbState", int.class);
@@ -1537,6 +1560,7 @@
* @see #CONTROL_EFFECT_MODE_BLACKBOARD
* @see #CONTROL_EFFECT_MODE_AQUA
*/
+ @PublicKey
public static final Key<Integer> CONTROL_EFFECT_MODE =
new Key<Integer>("android.control.effectMode", int.class);
@@ -1565,6 +1589,7 @@
* @see #CONTROL_MODE_USE_SCENE_MODE
* @see #CONTROL_MODE_OFF_KEEP_STATE
*/
+ @PublicKey
public static final Key<Integer> CONTROL_MODE =
new Key<Integer>("android.control.mode", int.class);
@@ -1606,6 +1631,7 @@
* @see #CONTROL_SCENE_MODE_BARCODE
* @see #CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO
*/
+ @PublicKey
public static final Key<Integer> CONTROL_SCENE_MODE =
new Key<Integer>("android.control.sceneMode", int.class);
@@ -1630,6 +1656,7 @@
* @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF
* @see #CONTROL_VIDEO_STABILIZATION_MODE_ON
*/
+ @PublicKey
public static final Key<Integer> CONTROL_VIDEO_STABILIZATION_MODE =
new Key<Integer>("android.control.videoStabilizationMode", int.class);
@@ -1650,6 +1677,7 @@
* @see #EDGE_MODE_FAST
* @see #EDGE_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> EDGE_MODE =
new Key<Integer>("android.edge.mode", int.class);
@@ -1677,6 +1705,7 @@
* @see #FLASH_MODE_SINGLE
* @see #FLASH_MODE_TORCH
*/
+ @PublicKey
public static final Key<Integer> FLASH_MODE =
new Key<Integer>("android.flash.mode", int.class);
@@ -1694,6 +1723,7 @@
* @see #FLASH_STATE_FIRED
* @see #FLASH_STATE_PARTIAL
*/
+ @PublicKey
public static final Key<Integer> FLASH_STATE =
new Key<Integer>("android.flash.state", int.class);
@@ -1710,12 +1740,15 @@
* @see #HOT_PIXEL_MODE_FAST
* @see #HOT_PIXEL_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> HOT_PIXEL_MODE =
new Key<Integer>("android.hotPixel.mode", int.class);
/**
* <p>A location object to use when generating image GPS metadata.</p>
*/
+ @PublicKey
+ @SyntheticKey
public static final Key<android.location.Location> JPEG_GPS_LOCATION =
new Key<android.location.Location>("android.jpeg.gpsLocation", android.location.Location.class);
@@ -1747,6 +1780,7 @@
* <p>Orientation of JPEG image to
* write</p>
*/
+ @PublicKey
public static final Key<Integer> JPEG_ORIENTATION =
new Key<Integer>("android.jpeg.orientation", int.class);
@@ -1755,6 +1789,7 @@
* image.</p>
* <p>85-95 is typical usage range.</p>
*/
+ @PublicKey
public static final Key<Byte> JPEG_QUALITY =
new Key<Byte>("android.jpeg.quality", byte.class);
@@ -1762,6 +1797,7 @@
* <p>Compression quality of JPEG
* thumbnail.</p>
*/
+ @PublicKey
public static final Key<Byte> JPEG_THUMBNAIL_QUALITY =
new Key<Byte>("android.jpeg.thumbnailQuality", byte.class);
@@ -1778,6 +1814,7 @@
* generate the thumbnail image. The thumbnail image will always have a smaller Field
* Of View (FOV) than the primary image when aspect ratios differ.</p>
*/
+ @PublicKey
public static final Key<android.util.Size> JPEG_THUMBNAIL_SIZE =
new Key<android.util.Size>("android.jpeg.thumbnailSize", android.util.Size.class);
@@ -1807,6 +1844,7 @@
* @see CaptureRequest#SENSOR_FRAME_DURATION
* @see CaptureRequest#SENSOR_SENSITIVITY
*/
+ @PublicKey
public static final Key<Float> LENS_APERTURE =
new Key<Float>("android.lens.aperture", float.class);
@@ -1830,6 +1868,7 @@
* @see CameraCharacteristics#LENS_INFO_AVAILABLE_FILTER_DENSITIES
* @see CaptureResult#LENS_STATE
*/
+ @PublicKey
public static final Key<Float> LENS_FILTER_DENSITY =
new Key<Float>("android.lens.filterDensity", float.class);
@@ -1849,6 +1888,7 @@
* @see CaptureRequest#LENS_FOCUS_DISTANCE
* @see CaptureResult#LENS_STATE
*/
+ @PublicKey
public static final Key<Float> LENS_FOCAL_LENGTH =
new Key<Float>("android.lens.focalLength", float.class);
@@ -1857,6 +1897,7 @@
* measured from frontmost surface of the lens.</p>
* <p>Should be zero for fixed-focus cameras</p>
*/
+ @PublicKey
public static final Key<Float> LENS_FOCUS_DISTANCE =
new Key<Float>("android.lens.focusDistance", float.class);
@@ -1866,6 +1907,7 @@
* <p>If variable focus not supported, can still report
* fixed depth of field range</p>
*/
+ @PublicKey
public static final Key<android.util.Pair<Float,Float>> LENS_FOCUS_RANGE =
new Key<android.util.Pair<Float,Float>>("android.lens.focusRange", new TypeReference<android.util.Pair<Float,Float>>() {{ }});
@@ -1895,6 +1937,7 @@
* @see #LENS_OPTICAL_STABILIZATION_MODE_OFF
* @see #LENS_OPTICAL_STABILIZATION_MODE_ON
*/
+ @PublicKey
public static final Key<Integer> LENS_OPTICAL_STABILIZATION_MODE =
new Key<Integer>("android.lens.opticalStabilizationMode", int.class);
@@ -1931,6 +1974,7 @@
* @see #LENS_STATE_STATIONARY
* @see #LENS_STATE_MOVING
*/
+ @PublicKey
public static final Key<Integer> LENS_STATE =
new Key<Integer>("android.lens.state", int.class);
@@ -1951,6 +1995,7 @@
* @see #NOISE_REDUCTION_MODE_FAST
* @see #NOISE_REDUCTION_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> NOISE_REDUCTION_MODE =
new Key<Integer>("android.noiseReduction.mode", int.class);
@@ -1982,6 +2027,7 @@
* frameCount value).</p>
* <p>Reset on release()</p>
*/
+ @PublicKey
public static final Key<Integer> REQUEST_FRAME_COUNT =
new Key<Integer>("android.request.frameCount", int.class);
@@ -2005,6 +2051,7 @@
*
* @see CameraCharacteristics#REQUEST_PIPELINE_MAX_DEPTH
*/
+ @PublicKey
public static final Key<Byte> REQUEST_PIPELINE_DEPTH =
new Key<Byte>("android.request.pipelineDepth", byte.class);
@@ -2043,6 +2090,7 @@
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
+ @PublicKey
public static final Key<android.graphics.Rect> SCALER_CROP_REGION =
new Key<android.graphics.Rect>("android.scaler.cropRegion", android.graphics.Rect.class);
@@ -2052,6 +2100,7 @@
* <p>If the sensor can't expose this exact duration, it should shorten the
* duration exposed to the nearest possible value (rather than expose longer).</p>
*/
+ @PublicKey
public static final Key<Long> SENSOR_EXPOSURE_TIME =
new Key<Long>("android.sensor.exposureTime", long.class);
@@ -2130,6 +2179,7 @@
*
* @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
*/
+ @PublicKey
public static final Key<Long> SENSOR_FRAME_DURATION =
new Key<Long>("android.sensor.frameDuration", long.class);
@@ -2149,6 +2199,7 @@
* @see CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE
* @see CameraCharacteristics#SENSOR_MAX_ANALOG_SENSITIVITY
*/
+ @PublicKey
public static final Key<Integer> SENSOR_SENSITIVITY =
new Key<Integer>("android.sensor.sensitivity", int.class);
@@ -2171,6 +2222,7 @@
*
* @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_CALIBRATION
*/
+ @PublicKey
public static final Key<Long> SENSOR_TIMESTAMP =
new Key<Long>("android.sensor.timestamp", long.class);
@@ -2185,10 +2237,38 @@
* <p>The order of the values is R, G, B; where R is in the lowest index.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*/
+ @PublicKey
public static final Key<Rational[]> SENSOR_NEUTRAL_COLOR_POINT =
new Key<Rational[]>("android.sensor.neutralColorPoint", Rational[].class);
/**
+ * <p>Noise model coefficients for each CFA mosaic channel.</p>
+ * <p>This tag contains two noise model coefficients for each CFA channel
+ * corresponding to the sensor amplification (S) and sensor readout
+ * noise (O). These are given as pairs of coefficients for each channel
+ * in the same order as channels listed for the CFA layout tag
+ * (see {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT android.sensor.info.colorFilterArrangement}). This is
+ * represented as an array of Pair<Double, Double>, where
+ * the first member of the Pair at index n is the S coefficient and the
+ * second member is the O coefficient for the nth color channel in the CFA.</p>
+ * <p>These coefficients are used in a two parameter noise model to describe
+ * the amount of noise present in the image for each CFA channel. The
+ * noise model used here is:</p>
+ * <p>N(x) = sqrt(Sx + O)</p>
+ * <p>Where x represents the recorded signal of a CFA channel normalized to
+ * the range [0, 1], and S and O are the noise model coeffiecients for
+ * that channel.</p>
+ * <p>A more detailed description of the noise model can be found in the
+ * Adobe DNG specification for the NoiseProfile tag.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
+ */
+ @PublicKey
+ public static final Key<android.util.Pair<Double,Double>[]> SENSOR_NOISE_PROFILE =
+ new Key<android.util.Pair<Double,Double>[]>("android.sensor.noiseProfile", new TypeReference<android.util.Pair<Double,Double>[]>() {{ }});
+
+ /**
* <p>The worst-case divergence between Bayer green channels.</p>
* <p>This value is an estimate of the worst case split between the
* Bayer green channels in the red and blue rows in the sensor color
@@ -2222,6 +2302,7 @@
* </ul>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*/
+ @PublicKey
public static final Key<Float> SENSOR_GREEN_SPLIT =
new Key<Float>("android.sensor.greenSplit", float.class);
@@ -2238,6 +2319,7 @@
*
* @see CaptureRequest#SENSOR_TEST_PATTERN_MODE
*/
+ @PublicKey
public static final Key<int[]> SENSOR_TEST_PATTERN_DATA =
new Key<int[]>("android.sensor.testPatternData", int[].class);
@@ -2258,6 +2340,7 @@
* @see #SENSOR_TEST_PATTERN_MODE_PN9
* @see #SENSOR_TEST_PATTERN_MODE_CUSTOM1
*/
+ @PublicKey
public static final Key<Integer> SENSOR_TEST_PATTERN_MODE =
new Key<Integer>("android.sensor.testPatternMode", int.class);
@@ -2272,6 +2355,7 @@
*
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
+ @PublicKey
public static final Key<Long> SENSOR_ROLLING_SHUTTER_SKEW =
new Key<Long>("android.sensor.rollingShutterSkew", long.class);
@@ -2310,6 +2394,7 @@
* @see #SHADING_MODE_FAST
* @see #SHADING_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> SHADING_MODE =
new Key<Integer>("android.shading.mode", int.class);
@@ -2326,6 +2411,7 @@
* @see #STATISTICS_FACE_DETECT_MODE_SIMPLE
* @see #STATISTICS_FACE_DETECT_MODE_FULL
*/
+ @PublicKey
public static final Key<Integer> STATISTICS_FACE_DETECT_MODE =
new Key<Integer>("android.statistics.faceDetectMode", int.class);
@@ -2388,6 +2474,8 @@
*
* @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
*/
+ @PublicKey
+ @SyntheticKey
public static final Key<android.hardware.camera2.params.Face[]> STATISTICS_FACES =
new Key<android.hardware.camera2.params.Face[]>("android.statistics.faces", android.hardware.camera2.params.Face[].class);
@@ -2434,6 +2522,7 @@
*
* @see CaptureRequest#COLOR_CORRECTION_MODE
*/
+ @PublicKey
public static final Key<android.hardware.camera2.params.LensShadingMap> STATISTICS_LENS_SHADING_CORRECTION_MAP =
new Key<android.hardware.camera2.params.LensShadingMap>("android.statistics.lensShadingCorrectionMap", android.hardware.camera2.params.LensShadingMap.class);
@@ -2554,6 +2643,7 @@
* @see #STATISTICS_SCENE_FLICKER_50HZ
* @see #STATISTICS_SCENE_FLICKER_60HZ
*/
+ @PublicKey
public static final Key<Integer> STATISTICS_SCENE_FLICKER =
new Key<Integer>("android.statistics.sceneFlicker", int.class);
@@ -2566,6 +2656,7 @@
* @see CaptureResult#STATISTICS_HOT_PIXEL_MAP
* @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES
*/
+ @PublicKey
public static final Key<Boolean> STATISTICS_HOT_PIXEL_MAP_MODE =
new Key<Boolean>("android.statistics.hotPixelMapMode", boolean.class);
@@ -2581,6 +2672,7 @@
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
*/
+ @PublicKey
public static final Key<android.graphics.Point[]> STATISTICS_HOT_PIXEL_MAP =
new Key<android.graphics.Point[]>("android.statistics.hotPixelMap", android.graphics.Point[].class);
@@ -2593,6 +2685,7 @@
* @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF
* @see #STATISTICS_LENS_SHADING_MAP_MODE_ON
*/
+ @PublicKey
public static final Key<Integer> STATISTICS_LENS_SHADING_MAP_MODE =
new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
@@ -2722,6 +2815,8 @@
* @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
* @see CaptureRequest#TONEMAP_MODE
*/
+ @PublicKey
+ @SyntheticKey
public static final Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE =
new Key<android.hardware.camera2.params.TonemapCurve>("android.tonemap.curve", android.hardware.camera2.params.TonemapCurve.class);
@@ -2755,6 +2850,7 @@
* @see #TONEMAP_MODE_FAST
* @see #TONEMAP_MODE_HIGH_QUALITY
*/
+ @PublicKey
public static final Key<Integer> TONEMAP_MODE =
new Key<Integer>("android.tonemap.mode", int.class);
@@ -2784,6 +2880,7 @@
*
* @see CaptureRequest#BLACK_LEVEL_LOCK
*/
+ @PublicKey
public static final Key<Boolean> BLACK_LEVEL_LOCK =
new Key<Boolean>("android.blackLevel.lock", boolean.class);
@@ -2851,4 +2948,5 @@
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
+
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 02be22f..fb1bc15 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -28,8 +28,6 @@
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
-import android.hardware.camera2.utils.CloseableLock;
-import android.hardware.camera2.utils.CloseableLock.ScopedLock;
import android.hardware.camera2.utils.LongParcelable;
import android.os.Handler;
import android.os.IBinder;
@@ -59,7 +57,8 @@
// TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
private ICameraDeviceUser mRemoteDevice;
- private final CloseableLock mCloseLock;
+ // Lock to synchronize cross-thread access to device public interface
+ private final Object mInterfaceLock = new Object();
private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
private final StateListener mDeviceListener;
@@ -104,60 +103,64 @@
private final Runnable mCallOnOpened = new Runnable() {
@Override
public void run() {
- try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
- if (scopedLock == null) return; // Camera already closed
+ StateListener sessionListener = null;
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) return; // Camera already closed
- StateListener sessionListener = mSessionStateListener;
- if (sessionListener != null) {
- sessionListener.onOpened(CameraDeviceImpl.this);
- }
- mDeviceListener.onOpened(CameraDeviceImpl.this);
+ sessionListener = mSessionStateListener;
}
+ if (sessionListener != null) {
+ sessionListener.onOpened(CameraDeviceImpl.this);
+ }
+ mDeviceListener.onOpened(CameraDeviceImpl.this);
}
};
private final Runnable mCallOnUnconfigured = new Runnable() {
@Override
public void run() {
- try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
- if (scopedLock == null) return; // Camera already closed
+ StateListener sessionListener = null;
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) return; // Camera already closed
- StateListener sessionListener = mSessionStateListener;
- if (sessionListener != null) {
- sessionListener.onUnconfigured(CameraDeviceImpl.this);
- }
- mDeviceListener.onUnconfigured(CameraDeviceImpl.this);
+ sessionListener = mSessionStateListener;
}
+ if (sessionListener != null) {
+ sessionListener.onUnconfigured(CameraDeviceImpl.this);
+ }
+ mDeviceListener.onUnconfigured(CameraDeviceImpl.this);
}
};
private final Runnable mCallOnActive = new Runnable() {
@Override
public void run() {
- try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
- if (scopedLock == null) return; // Camera already closed
+ StateListener sessionListener = null;
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) return; // Camera already closed
- StateListener sessionListener = mSessionStateListener;
- if (sessionListener != null) {
- sessionListener.onActive(CameraDeviceImpl.this);
- }
- mDeviceListener.onActive(CameraDeviceImpl.this);
+ sessionListener = mSessionStateListener;
}
+ if (sessionListener != null) {
+ sessionListener.onActive(CameraDeviceImpl.this);
+ }
+ mDeviceListener.onActive(CameraDeviceImpl.this);
}
};
private final Runnable mCallOnBusy = new Runnable() {
@Override
public void run() {
- try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
- if (scopedLock == null) return; // Camera already closed
+ StateListener sessionListener = null;
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) return; // Camera already closed
- StateListener sessionListener = mSessionStateListener;
- if (sessionListener != null) {
- sessionListener.onBusy(CameraDeviceImpl.this);
- }
- mDeviceListener.onBusy(CameraDeviceImpl.this);
+ sessionListener = mSessionStateListener;
}
+ if (sessionListener != null) {
+ sessionListener.onBusy(CameraDeviceImpl.this);
+ }
+ mDeviceListener.onBusy(CameraDeviceImpl.this);
}
};
@@ -169,8 +172,10 @@
if (mClosedOnce) {
throw new AssertionError("Don't post #onClosed more than once");
}
-
- StateListener sessionListener = mSessionStateListener;
+ StateListener sessionListener = null;
+ synchronized(mInterfaceLock) {
+ sessionListener = mSessionStateListener;
+ }
if (sessionListener != null) {
sessionListener.onClosed(CameraDeviceImpl.this);
}
@@ -182,30 +187,32 @@
private final Runnable mCallOnIdle = new Runnable() {
@Override
public void run() {
- try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
- if (scopedLock == null) return; // Camera already closed
+ StateListener sessionListener = null;
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) return; // Camera already closed
- StateListener sessionListener = mSessionStateListener;
- if (sessionListener != null) {
- sessionListener.onIdle(CameraDeviceImpl.this);
- }
- mDeviceListener.onIdle(CameraDeviceImpl.this);
+ sessionListener = mSessionStateListener;
}
+ if (sessionListener != null) {
+ sessionListener.onIdle(CameraDeviceImpl.this);
+ }
+ mDeviceListener.onIdle(CameraDeviceImpl.this);
}
};
private final Runnable mCallOnDisconnected = new Runnable() {
@Override
public void run() {
- try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
- if (scopedLock == null) return; // Camera already closed
+ StateListener sessionListener = null;
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) return; // Camera already closed
- StateListener sessionListener = mSessionStateListener;
- if (sessionListener != null) {
- sessionListener.onDisconnected(CameraDeviceImpl.this);
- }
- mDeviceListener.onDisconnected(CameraDeviceImpl.this);
+ sessionListener = mSessionStateListener;
}
+ if (sessionListener != null) {
+ sessionListener.onDisconnected(CameraDeviceImpl.this);
+ }
+ mDeviceListener.onDisconnected(CameraDeviceImpl.this);
}
};
@@ -218,7 +225,6 @@
mDeviceListener = listener;
mDeviceHandler = handler;
mCharacteristics = characteristics;
- mCloseLock = new CloseableLock(/*name*/"CD-" + mCameraId);
final int MAX_TAG_LEN = 23;
String tag = String.format("CameraDevice-JV-%s", mCameraId);
@@ -243,8 +249,8 @@
}
public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
- try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
- // TODO: Move from decorator to direct binder-mediated exceptions
+ synchronized(mInterfaceLock) {
+ // TODO: Move from decorator to direct binder-mediated exceptions
// If setRemoteFailure already called, do nothing
if (mInError) return;
@@ -287,8 +293,8 @@
}
final int code = failureCode;
final boolean isError = failureIsError;
- try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
- if (scopedLock == null) return; // Camera already closed, can't go to error state
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) return; // Camera already closed, can't go to error state
mInError = true;
mDeviceHandler.post(new Runnable() {
@@ -314,7 +320,7 @@
if (outputs == null) {
outputs = new ArrayList<Surface>();
}
- try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
+ synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
@@ -378,7 +384,7 @@
public void createCaptureSession(List<Surface> outputs,
CameraCaptureSession.StateListener listener, Handler handler)
throws CameraAccessException {
- try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
+ synchronized(mInterfaceLock) {
if (DEBUG) {
Log.d(TAG, "createCaptureSession");
}
@@ -423,7 +429,7 @@
@Override
public CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException {
- try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
+ synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
CameraMetadataNative templatedRequest = new CameraMetadataNative();
@@ -554,7 +560,7 @@
}
}
- try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
+ synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
int requestId;
@@ -623,7 +629,7 @@
public void stopRepeating() throws CameraAccessException {
- try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
+ synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
if (mRepeatingRequestId != REQUEST_ID_NONE) {
@@ -654,12 +660,12 @@
private void waitUntilIdle() throws CameraAccessException {
- try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
+ synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
+
if (mRepeatingRequestId != REQUEST_ID_NONE) {
throw new IllegalStateException("Active repeating request ongoing");
}
-
try {
mRemoteDevice.waitUntilIdle();
} catch (CameraRuntimeException e) {
@@ -668,13 +674,11 @@
// impossible
return;
}
-
- mRepeatingRequestId = REQUEST_ID_NONE;
}
}
public void flush() throws CameraAccessException {
- try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
+ synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
mDeviceHandler.post(mCallOnBusy);
@@ -697,15 +701,7 @@
@Override
public void close() {
- mClosing = true;
- // Acquire exclusive lock, close, release (idempotent)
- mCloseLock.close();
-
- /*
- * The rest of this is safe, since no other methods will be able to execute
- * (they will throw ISE instead; the callbacks will get dropped)
- */
- {
+ synchronized (mInterfaceLock) {
try {
if (mRemoteDevice != null) {
mRemoteDevice.disconnect();
@@ -853,8 +849,8 @@
// remove request from mCaptureListenerMap
final int requestId = frameNumberRequestPair.getValue();
final CaptureListenerHolder holder;
- try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
- if (scopedLock == null) {
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) {
Log.w(TAG, "Camera closed while checking sequences");
return;
}
@@ -944,8 +940,8 @@
public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
Runnable r = null;
- try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
- if (scopedLock == null) {
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) {
return; // Camera already closed
}
@@ -985,8 +981,8 @@
if (DEBUG) {
Log.d(TAG, "Camera now idle");
}
- try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
- if (scopedLock == null) return; // Camera already closed
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) return; // Camera already closed
if (!CameraDeviceImpl.this.mIdle) {
CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
@@ -1003,8 +999,8 @@
}
final CaptureListenerHolder holder;
- try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
- if (scopedLock == null) return; // Camera already closed
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) return; // Camera already closed
// Get the listener for this frame ID, if there is one
holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
@@ -1042,8 +1038,8 @@
+ requestId);
}
- try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
- if (scopedLock == null) return; // Camera already closed
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) return; // Camera already closed
// TODO: Handle CameraCharacteristics access from CaptureResult correctly.
result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
diff --git a/core/java/android/hardware/camera2/impl/ListenerProxies.java b/core/java/android/hardware/camera2/impl/ListenerProxies.java
index 04c43e3..ab9a4d5 100644
--- a/core/java/android/hardware/camera2/impl/ListenerProxies.java
+++ b/core/java/android/hardware/camera2/impl/ListenerProxies.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package android.hardware.camera2.impl;
import android.hardware.camera2.CameraCaptureSession;
diff --git a/core/java/android/hardware/camera2/impl/PublicKey.java b/core/java/android/hardware/camera2/impl/PublicKey.java
new file mode 100644
index 0000000..d894c1f
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/PublicKey.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.impl;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denote a static field {@code Key} as being public-visible (in the SDK).
+ *
+ * <p>Keys without this annotated are assumed to be {@code @hide}.</p>
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface PublicKey {
+
+}
diff --git a/core/java/android/hardware/camera2/impl/SyntheticKey.java b/core/java/android/hardware/camera2/impl/SyntheticKey.java
new file mode 100644
index 0000000..034a04939
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/SyntheticKey.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.impl;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denote a static field {@code Key} as being synthetic (i.e. not having a native
+ * tag one-to-one counterpart).
+ *
+ * <p>Keys without this annotated are assumed to always have a native counter-part.</p>
+ *
+ * <p>In particular a key with a native counter-part will always have it's {@code #getTag}
+ * method succeed.</p>
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface SyntheticKey {
+
+}
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 5bdef4a..c68d8c3 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -25,11 +25,15 @@
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.utils.LongParcelable;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.CaptureResultExtras;
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
import android.os.ConditionVariable;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
@@ -66,14 +70,18 @@
private final SparseArray<Surface> mSurfaces;
private final CameraCharacteristics mCameraCharacteristics;
private final CameraLooper mCameraInit;
+ private final CameraCallbackThread mCameraCallbacks;
+
protected CameraDeviceUserShim(int cameraId, LegacyCameraDevice legacyCamera,
- CameraCharacteristics characteristics, CameraLooper cameraInit) {
+ CameraCharacteristics characteristics, CameraLooper cameraInit,
+ CameraCallbackThread cameraCallbacks) {
mLegacyDevice = legacyCamera;
mConfiguring = false;
mSurfaces = new SparseArray<Surface>();
mCameraCharacteristics = characteristics;
mCameraInit = cameraInit;
+ mCameraCallbacks = cameraCallbacks;
mSurfaceIdCounter = 0;
}
@@ -173,6 +181,122 @@
}
}
+ /**
+ * A thread to process callbacks to send back to the camera client.
+ *
+ * <p>This effectively emulates one-way binder semantics when in the same process as the
+ * callee.</p>
+ */
+ private static class CameraCallbackThread implements ICameraDeviceCallbacks {
+ private static final int CAMERA_ERROR = 0;
+ private static final int CAMERA_IDLE = 1;
+ private static final int CAPTURE_STARTED = 2;
+ private static final int RESULT_RECEIVED = 3;
+
+ private final HandlerThread mHandlerThread;
+ private Handler mHandler;
+
+ private final ICameraDeviceCallbacks mCallbacks;
+
+ public CameraCallbackThread(ICameraDeviceCallbacks callbacks) {
+ mCallbacks = callbacks;
+
+ mHandlerThread = new HandlerThread("LegacyCameraCallback");
+ mHandlerThread.start();
+ }
+
+ public void close() {
+ mHandlerThread.quitSafely();
+ }
+
+ @Override
+ public void onCameraError(final int errorCode, final CaptureResultExtras resultExtras) {
+ Message msg = getHandler().obtainMessage(CAMERA_ERROR,
+ /*arg1*/ errorCode, /*arg2*/ 0,
+ /*obj*/ resultExtras);
+ getHandler().sendMessage(msg);
+ }
+
+ @Override
+ public void onCameraIdle() {
+ Message msg = getHandler().obtainMessage(CAMERA_IDLE);
+ getHandler().sendMessage(msg);
+ }
+
+ @Override
+ public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
+ Message msg = getHandler().obtainMessage(CAPTURE_STARTED,
+ /*arg1*/ (int) (timestamp & 0xFFFFFFFFL),
+ /*arg2*/ (int) ( (timestamp >> 32) & 0xFFFFFFFFL),
+ /*obj*/ resultExtras);
+ getHandler().sendMessage(msg);
+ }
+
+ @Override
+ public void onResultReceived(final CameraMetadataNative result,
+ final CaptureResultExtras resultExtras) {
+ Object[] resultArray = new Object[] { result, resultExtras };
+ Message msg = getHandler().obtainMessage(RESULT_RECEIVED,
+ /*obj*/ resultArray);
+ getHandler().sendMessage(msg);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ // This is solely intended to be used for in-process binding.
+ return null;
+ }
+
+ private Handler getHandler() {
+ if (mHandler == null) {
+ mHandler = new CallbackHandler(mHandlerThread.getLooper());
+ }
+ return mHandler;
+ }
+
+ private class CallbackHandler extends Handler {
+ public CallbackHandler(Looper l) {
+ super(l);
+ }
+
+ public void handleMessage(Message msg) {
+ try {
+ switch (msg.what) {
+ case CAMERA_ERROR: {
+ int errorCode = msg.arg1;
+ CaptureResultExtras resultExtras = (CaptureResultExtras) msg.obj;
+ mCallbacks.onCameraError(errorCode, resultExtras);
+ break;
+ }
+ case CAMERA_IDLE:
+ mCallbacks.onCameraIdle();
+ break;
+ case CAPTURE_STARTED: {
+ long timestamp = msg.arg2 & 0xFFFFFFFFL;
+ timestamp = (timestamp << 32) | (msg.arg1 & 0xFFFFFFFFL);
+ CaptureResultExtras resultExtras = (CaptureResultExtras) msg.obj;
+ mCallbacks.onCaptureStarted(resultExtras, timestamp);
+ break;
+ }
+ case RESULT_RECEIVED: {
+ Object[] resultArray = (Object[]) msg.obj;
+ CameraMetadataNative result = (CameraMetadataNative) resultArray[0];
+ CaptureResultExtras resultExtras = (CaptureResultExtras) resultArray[1];
+ mCallbacks.onResultReceived(result, resultExtras);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException(
+ "Unknown callback message " + msg.what);
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Received remote exception during camera callback " + msg.what, e);
+ }
+ }
+ }
+ }
+
public static CameraDeviceUserShim connectBinderShim(ICameraDeviceCallbacks callbacks,
int cameraId) {
if (DEBUG) {
@@ -187,6 +311,8 @@
CameraLooper init = new CameraLooper(cameraId);
+ CameraCallbackThread threadCallbacks = new CameraCallbackThread(callbacks);
+
// TODO: Make this async instead of blocking
int initErrors = init.waitForOpen(OPEN_CAMERA_TIMEOUT_MS);
Camera legacyCamera = init.getCamera();
@@ -200,8 +326,8 @@
CameraCharacteristics characteristics =
LegacyMetadataMapper.createCharacteristics(legacyCamera.getParameters(), info);
LegacyCameraDevice device = new LegacyCameraDevice(
- cameraId, legacyCamera, characteristics, callbacks);
- return new CameraDeviceUserShim(cameraId, device, characteristics, init);
+ cameraId, legacyCamera, characteristics, threadCallbacks);
+ return new CameraDeviceUserShim(cameraId, device, characteristics, init, threadCallbacks);
}
@Override
@@ -214,6 +340,7 @@
mLegacyDevice.close();
} finally {
mCameraInit.close();
+ mCameraCallbacks.close();
}
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 519bbb6..157c159 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -25,6 +25,7 @@
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.params.StreamConfigurationDuration;
@@ -444,6 +445,120 @@
m.set(REQUEST_AVAILABLE_CAPABILITIES, capabilities);
/*
+ * request.availableCharacteristicsKeys
+ */
+ {
+ // TODO: check if the underlying key is supported before listing a key as available
+
+ // Note: We only list public keys. Native HALs should list ALL keys regardless of visibility.
+
+ Key<?> availableKeys[] = new Key<?>[] {
+ CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES ,
+ CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES ,
+ CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES ,
+ CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE ,
+ CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP ,
+ CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES ,
+ CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS ,
+ CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES ,
+ CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES ,
+ CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES ,
+ CameraCharacteristics.CONTROL_MAX_REGIONS ,
+ CameraCharacteristics.FLASH_INFO_AVAILABLE ,
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL ,
+ CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES ,
+ CameraCharacteristics.LENS_FACING ,
+ CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS ,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES ,
+ CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS ,
+ CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT ,
+ CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH ,
+ CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM ,
+// CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP ,
+ CameraCharacteristics.SCALER_CROPPING_TYPE ,
+ CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES ,
+ CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE ,
+ CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE ,
+ CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE ,
+ CameraCharacteristics.SENSOR_ORIENTATION ,
+ CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES ,
+ CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT ,
+ CameraCharacteristics.SYNC_MAX_LATENCY ,
+ };
+ m.set(REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, getTagsForKeys(availableKeys));
+ }
+
+ /*
+ * request.availableRequestKeys
+ */
+ {
+ CaptureRequest.Key<?> availableKeys[] = new CaptureRequest.Key<?>[] {
+ CaptureRequest.CONTROL_AE_ANTIBANDING_MODE,
+ CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION,
+ CaptureRequest.CONTROL_AE_LOCK,
+ CaptureRequest.CONTROL_AE_MODE,
+ CaptureRequest.CONTROL_AE_REGIONS,
+ CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
+ CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_REGIONS,
+ CaptureRequest.CONTROL_AF_TRIGGER,
+ CaptureRequest.CONTROL_AWB_LOCK,
+ CaptureRequest.CONTROL_AWB_MODE,
+ CaptureRequest.CONTROL_CAPTURE_INTENT,
+ CaptureRequest.CONTROL_EFFECT_MODE,
+ CaptureRequest.CONTROL_MODE,
+ CaptureRequest.CONTROL_SCENE_MODE,
+ CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
+ CaptureRequest.FLASH_MODE,
+ CaptureRequest.JPEG_GPS_COORDINATES,
+ CaptureRequest.JPEG_GPS_PROCESSING_METHOD,
+ CaptureRequest.JPEG_GPS_TIMESTAMP,
+ CaptureRequest.JPEG_ORIENTATION,
+ CaptureRequest.JPEG_QUALITY,
+ CaptureRequest.JPEG_THUMBNAIL_QUALITY,
+ CaptureRequest.JPEG_THUMBNAIL_SIZE,
+ CaptureRequest.LENS_FOCAL_LENGTH,
+ CaptureRequest.SCALER_CROP_REGION,
+ CaptureRequest.STATISTICS_FACE_DETECT_MODE,
+ };
+ m.set(REQUEST_AVAILABLE_REQUEST_KEYS, getTagsForKeys(availableKeys));
+ }
+
+ /*
+ * request.availableResultKeys
+ */
+ {
+ CaptureResult.Key<?> availableKeys[] = new CaptureResult.Key<?>[] {
+ CaptureResult.CONTROL_AE_ANTIBANDING_MODE ,
+ CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION ,
+ CaptureResult.CONTROL_AE_LOCK ,
+ CaptureResult.CONTROL_AE_MODE ,
+ CaptureResult.CONTROL_AE_REGIONS ,
+ CaptureResult.CONTROL_AF_MODE ,
+ CaptureResult.CONTROL_AF_REGIONS ,
+ CaptureResult.CONTROL_AF_STATE ,
+ CaptureResult.CONTROL_AWB_MODE ,
+ CaptureResult.CONTROL_AWB_LOCK ,
+ CaptureResult.CONTROL_MODE ,
+ CaptureResult.FLASH_MODE ,
+ CaptureResult.JPEG_GPS_COORDINATES ,
+ CaptureResult.JPEG_GPS_PROCESSING_METHOD ,
+ CaptureResult.JPEG_GPS_TIMESTAMP ,
+ CaptureResult.JPEG_ORIENTATION ,
+ CaptureResult.JPEG_QUALITY ,
+ CaptureResult.JPEG_THUMBNAIL_QUALITY ,
+ CaptureResult.LENS_FOCAL_LENGTH ,
+ CaptureResult.REQUEST_FRAME_COUNT ,
+ CaptureResult.REQUEST_PIPELINE_DEPTH ,
+ CaptureResult.SCALER_CROP_REGION ,
+ CaptureResult.SENSOR_TIMESTAMP ,
+ CaptureResult.STATISTICS_FACE_DETECT_MODE ,
+// CaptureResult.STATISTICS_FACES ,
+ };
+ m.set(REQUEST_AVAILABLE_RESULT_KEYS, getTagsForKeys(availableKeys));
+ }
+
+ /*
* request.maxNumOutputStreams
*/
int[] outputStreams = {
@@ -649,4 +764,34 @@
// TODO: map other request template values
return m;
}
+
+ private static int[] getTagsForKeys(Key<?>[] keys) {
+ int[] tags = new int[keys.length];
+
+ for (int i = 0; i < keys.length; ++i) {
+ tags[i] = keys[i].getNativeKey().getTag();
+ }
+
+ return tags;
+ }
+
+ private static int[] getTagsForKeys(CaptureRequest.Key<?>[] keys) {
+ int[] tags = new int[keys.length];
+
+ for (int i = 0; i < keys.length; ++i) {
+ tags[i] = keys[i].getNativeKey().getTag();
+ }
+
+ return tags;
+ }
+
+ private static int[] getTagsForKeys(CaptureResult.Key<?>[] keys) {
+ int[] tags = new int[keys.length];
+
+ for (int i = 0; i < keys.length; ++i) {
+ tags[i] = keys[i].getNativeKey().getTag();
+ }
+
+ return tags;
+ }
}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index f2426e5..1b9a0c5 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -471,22 +471,30 @@
private VirtualDisplayCallbacksDelegate mDelegate;
public VirtualDisplayCallbacks(VirtualDisplay.Callbacks callbacks, Handler handler) {
- mDelegate = new VirtualDisplayCallbacksDelegate(callbacks, handler);
+ if (callbacks != null) {
+ mDelegate = new VirtualDisplayCallbacksDelegate(callbacks, handler);
+ }
}
@Override // Binder call
public void onDisplayPaused() {
- mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_PAUSED);
+ if (mDelegate != null) {
+ mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_PAUSED);
+ }
}
@Override // Binder call
public void onDisplayResumed() {
- mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_RESUMED);
+ if (mDelegate != null) {
+ mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_RESUMED);
+ }
}
@Override // Binder call
public void onDisplayStopped() {
- mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_STOPPED);
+ if (mDelegate != null) {
+ mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_STOPPED);
+ }
}
}
@@ -505,9 +513,6 @@
@Override
public void handleMessage(Message msg) {
- if (mCallbacks == null) {
- return;
- }
switch (msg.what) {
case MSG_DISPLAY_PAUSED:
mCallbacks.onDisplayPaused();
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index e58c54d..99af2e7 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -19,6 +19,7 @@
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.PowerManager;
+import android.view.Display;
import android.view.DisplayInfo;
/**
@@ -132,13 +133,19 @@
* have to micro-manage screen off animations, auto-brightness and other effects.
*/
public static final class DisplayPowerRequest {
- public static final int SCREEN_STATE_OFF = 0;
- public static final int SCREEN_STATE_DOZE = 1;
- public static final int SCREEN_STATE_DIM = 2;
- public static final int SCREEN_STATE_BRIGHT = 3;
+ // Policy: Turn screen off as if the user pressed the power button
+ // including playing a screen off animation if applicable.
+ public static final int POLICY_OFF = 0;
+ // Policy: Enable dozing and always-on display functionality.
+ public static final int POLICY_DOZE = 1;
+ // Policy: Make the screen dim when the user activity timeout is
+ // about to expire.
+ public static final int POLICY_DIM = 2;
+ // Policy: Make the screen bright as usual.
+ public static final int POLICY_BRIGHT = 3;
- // The requested minimum screen power state: off, doze, dim or bright.
- public int screenState;
+ // The basic overall policy to apply: off, doze, dim or bright.
+ public int policy;
// If true, the proximity sensor overrides the screen state when an object is
// nearby, turning it off temporarily until the object is moved away.
@@ -169,44 +176,39 @@
// visible to the user.
public boolean blockScreenOn;
+ // Overrides the policy for adjusting screen brightness and state while dozing.
+ public int dozeScreenBrightness;
+ public int dozeScreenState;
+
public DisplayPowerRequest() {
- screenState = SCREEN_STATE_BRIGHT;
+ policy = POLICY_BRIGHT;
useProximitySensor = false;
screenBrightness = PowerManager.BRIGHTNESS_ON;
screenAutoBrightnessAdjustment = 0.0f;
useAutoBrightness = false;
blockScreenOn = false;
+ dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+ dozeScreenState = Display.STATE_UNKNOWN;
}
public DisplayPowerRequest(DisplayPowerRequest other) {
copyFrom(other);
}
- // Returns true if we want the screen on in any mode, including doze.
- public boolean wantScreenOnAny() {
- return screenState != SCREEN_STATE_OFF;
- }
-
- // Returns true if we want the screen on in a normal mode, excluding doze.
- // This is usually what we want to tell the rest of the system. For compatibility
- // reasons, we pretend the screen is off when dozing.
- public boolean wantScreenOnNormal() {
- return screenState == SCREEN_STATE_DIM || screenState == SCREEN_STATE_BRIGHT;
- }
-
- public boolean wantLightSensorEnabled() {
- // Specifically, we don't want the light sensor while dozing.
- return useAutoBrightness && wantScreenOnNormal();
+ public boolean isBrightOrDim() {
+ return policy == POLICY_BRIGHT || policy == POLICY_DIM;
}
public void copyFrom(DisplayPowerRequest other) {
- screenState = other.screenState;
+ policy = other.policy;
useProximitySensor = other.useProximitySensor;
screenBrightness = other.screenBrightness;
screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment;
useAutoBrightness = other.useAutoBrightness;
blockScreenOn = other.blockScreenOn;
lowPowerMode = other.lowPowerMode;
+ dozeScreenBrightness = other.dozeScreenBrightness;
+ dozeScreenState = other.dozeScreenState;
}
@Override
@@ -217,13 +219,15 @@
public boolean equals(DisplayPowerRequest other) {
return other != null
- && screenState == other.screenState
+ && policy == other.policy
&& useProximitySensor == other.useProximitySensor
&& screenBrightness == other.screenBrightness
&& screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment
&& useAutoBrightness == other.useAutoBrightness
&& blockScreenOn == other.blockScreenOn
- && lowPowerMode == other.lowPowerMode;
+ && lowPowerMode == other.lowPowerMode
+ && dozeScreenBrightness == other.dozeScreenBrightness
+ && dozeScreenState == other.dozeScreenState;
}
@Override
@@ -233,13 +237,30 @@
@Override
public String toString() {
- return "screenState=" + screenState
+ return "policy=" + policyToString(policy)
+ ", useProximitySensor=" + useProximitySensor
+ ", screenBrightness=" + screenBrightness
+ ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment
+ ", useAutoBrightness=" + useAutoBrightness
+ ", blockScreenOn=" + blockScreenOn
- + ", lowPowerMode=" + lowPowerMode;
+ + ", lowPowerMode=" + lowPowerMode
+ + ", dozeScreenBrightness=" + dozeScreenBrightness
+ + ", dozeScreenState=" + Display.stateToString(dozeScreenState);
+ }
+
+ public static String policyToString(int policy) {
+ switch (policy) {
+ case POLICY_OFF:
+ return "OFF";
+ case POLICY_DOZE:
+ return "DOZE";
+ case POLICY_DIM:
+ return "DIM";
+ case POLICY_BRIGHT:
+ return "BRIGHT";
+ default:
+ return Integer.toString(policy);
+ }
}
}
diff --git a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
index ae0bda1..acf92f1 100644
--- a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
@@ -57,7 +57,13 @@
// Value indicating the device is not an active source.
public static final int DEVICE_INACTIVE = -1;
- // Logical address, phsical address, device type, vendor id and display name
+ /**
+ * Logical address used to indicate the source comes from internal device.
+ * The logical address of TV(0) is used.
+ */
+ public static final int ADDR_INTERNAL = 0;
+
+ // Logical address, physical address, device type, vendor id and display name
// are immutable value.
private final int mLogicalAddress;
private final int mPhysicalAddress;
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 56fc1d6..521a439 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -69,6 +69,76 @@
public static final int RESULT_INCORRECT_MODE = 6;
public static final int RESULT_COMMUNICATION_FAILED = 7;
+ // -- Message ids for display osd.
+
+ /** Place holder for recording status message. Indicates the status of a recording. */
+ public static final int MESSAGE_RECORDING_STATUS_MESSAGE_START = 0x100;
+ /** Recording currently selected source. Indicates the status of a recording. */
+ public static final int MESSAGE_RECORDING_CURRENTLY_SELECTED_SOURCE = 0x101;
+ /** Recording Digital Service. Indicates the status of a recording. */
+ public static final int MESSAGE_RECORDING_DIGITAL_SERVICE = 0x102;
+ /** Recording Analogue Service. Indicates the status of a recording. */
+ public static final int MESSAGE_RECORDING_ANALOGUE_SERVICE = 0x103;
+ /** Recording External input. Indicates the status of a recording. */
+ public static final int MESSAGE_RECORDING_EXTERNAL_INPUT = 0x104;
+ /** No recording – unable to record Digital Service. No suitable tuner. */
+ public static final int MESSAGE_NO_RECORDNIG_UNABLE_DIGITAL_SERVICE = 0x105;
+ /** No recording – unable to record Analogue Service. No suitable tuner. */
+ public static final int MESSAGE_NO_RECORDNIG_UNABLE_ANALOGUE_SERVICE = 0x106;
+ /**
+ * No recording – unable to select required service. as suitable tuner, but the requested
+ * parameters are invalid or out of range for that tuner.
+ */
+ public static final int MESSAGE_NO_RECORDNIG_UNABLE_SELECTED_SERVICE = 0x107;
+ /** No recording – invalid External plug number */
+ public static final int MESSAGE_NO_RECORDNIG_INVALID_EXTERNAL_PLUG_NUMBER = 0x109;
+ /** No recording – invalid External Physical Address */
+ public static final int MESSAGE_NO_RECORDNIG_INVALID_EXTERNAL_PHYSICAL_ADDRESS = 0x10A;
+ /** No recording – CA system not supported */
+ public static final int MESSAGE_NO_RECORDNIG_UNSUPPORTED_CA = 0x10B;
+ /** No Recording – No or Insufficient CA Entitlements” */
+ public static final int MESSAGE_NO_RECORDNIG_NO_OR_INSUFFICIENT_CA_ENTITLEMENTS = 0x10C;
+ /** No recording – Not allowed to copy source. Source is “copy never”. */
+ public static final int MESSAGE_NO_RECORDNIG_DISALLOW_TO_COPY = 0x10D;
+ /** No recording – No further copies allowed */
+ public static final int MESSAGE_NO_RECORDNIG_DISALLOW_TO_FUTHER_COPIES = 0x10E;
+ /** No recording – No media */
+ public static final int MESSAGE_NO_RECORDNIG_NO_MEDIA = 0x110;
+ /** No recording – playing */
+ public static final int MESSAGE_NO_RECORDNIG_PLAYING = 0x111;
+ /** No recording – already recording */
+ public static final int MESSAGE_NO_RECORDNIG_ALREADY_RECORDING = 0x112;
+ /** No recording – media protected */
+ public static final int MESSAGE_NO_RECORDNIG_MEDIA_PROTECTED = 0x113;
+ /** No recording – no source signal */
+ public static final int MESSAGE_NO_RECORDNIG_NO_SOURCE_SIGNAL = 0x114;
+ /** No recording – media problem */
+ public static final int MESSAGE_NO_RECORDNIG_MEDIA_PROBLEM = 0x115;
+ /** No recording – not enough space available */
+ public static final int MESSAGE_NO_RECORDNIG_NOT_ENOUGH_SPACE = 0x116;
+ /** No recording – Parental Lock On */
+ public static final int MESSAGE_NO_RECORDNIG_PARENT_LOCK_ON = 0x117;
+ /** Recording terminated normally */
+ public static final int MESSAGE_RECORDING_TERMINATED_NORMALLY = 0x11A;
+ /** Recording has already terminated */
+ public static final int MESSAGE_RECORDING_ALREADY_TERMINATED = 0x11B;
+ /** No recording – other reason */
+ public static final int MESSAGE_NO_RECORDNIG_OTHER_REASON = 0x11F;
+ // From here extra message for recording that is not mentioned in CEC spec
+ /** No recording. Previous recording request in progress. */
+ public static final int MESSAGE_NO_RECORDING_PREVIOUS_RECORDING_IN_PROGRESS = 0x130;
+ /** No recording. Please check recorder and connection. */
+ public static final int MESSAGE_NO_RECORDING_CHECK_RECORDER_CONNECTION = 0x131;
+ /** Cannot record currently displayed source. */
+ public static final int MESSAGE_NO_RECORDING_FAIL_TO_RECORD_DISPLAYED_SCREEN = 0x132;
+
+ /** Timer recording type for digital service source. */
+ public static final int TIMER_RECORDING_TYPE_DIGITAL = 1;
+ /** Timer recording type for analogue service source. */
+ public static final int TIMER_RECORDING_TYPE_ANALOGUE = 2;
+ /** Timer recording type for external source. */
+ public static final int TIMER_RECORDING_TYPE_EXTERNAL = 3;
+
// True if we have a logical device of type playback hosted in the system.
private final boolean mHasPlaybackDevice;
// True if we have a logical device of type TV hosted in the system.
diff --git a/core/java/android/hardware/hdmi/HdmiRecordSources.java b/core/java/android/hardware/hdmi/HdmiRecordSources.java
new file mode 100644
index 0000000..296cae6
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiRecordSources.java
@@ -0,0 +1,772 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.hdmi;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.util.Log;
+
+/**
+ * Container for record source used for one touch record.
+ * Use one of helper method by source type.
+ * <ul>
+ * <li>Own source: {@link #ofOwnSource()}
+ * <li>Digital service(channel id): {@link #ofDigitalChannelId(int, DigitalChannelData)}
+ * <li>Digital service(ARIB): {@link #ofArib(int, AribData)}
+ * <li>Digital service(ATSC): {@link #ofAtsc(int, AtscData)}
+ * <li>Digital service(DVB): {@link #ofDvb(int, DvbData)}
+ * <li>Analogue: {@link #ofAnalogue(int, int, int)}
+ * <li>External plug: {@link #ofExternalPlug(int)}
+ * <li>External physical address: {@link #ofExternalPhysicalAddress(int)}.
+ * <ul>
+ *
+ * @hide
+ */
+@SystemApi
+public final class HdmiRecordSources {
+ private static final String TAG = "HdmiRecordSources";
+
+ /** Record source type for "Own Source". */
+ private static final int RECORD_SOURCE_TYPE_OWN_SOURCE = 1;
+ /** Record source type for "Digital Service". */
+ private static final int RECORD_SOURCE_TYPE_DIGITAL_SERVICE = 2;
+ /** Record source type for "Analogue Service". */
+ private static final int RECORD_SOURCE_TYPE_ANALOGUE_SERVICE = 3;
+ /** Record source type for "Exteranl Plug". */
+ private static final int RECORD_SOURCE_TYPE_EXTERNAL_PLUG = 4;
+ /** Record source type for "External Physical Address". */
+ private static final int RECORD_SOURCE_TYPE_EXTERNAL_PHYSICAL_ADDRESS = 5;
+
+ private HdmiRecordSources() {}
+
+ /**
+ * Base class for each record source.
+ */
+ static abstract class RecordSource {
+ protected final int mSourceType;
+ protected final int mExtraDataSize;
+
+ protected RecordSource(int sourceType, int extraDataSize) {
+ mSourceType = sourceType;
+ mExtraDataSize = extraDataSize;
+ }
+
+ abstract int extraParamToByteArray(byte[] data, int index);
+
+ final int getDataSize(boolean includeType) {
+ return includeType ? mExtraDataSize + 1 : mExtraDataSize;
+ }
+
+ public final int toByteArray(boolean includeType, byte[] data, int index) {
+ if (includeType) {
+ // 1 to 8 bytes (depends on source).
+ // {[Record Source Type]} |
+ // {[Record Source Type] [Digital Service Identification]} |
+ // {[Record Source Type] [Analogue Broadcast Type] [Analogue Frequency]
+ // [Broadcast System]} |
+ // {[Record Source Type] [External Plug]} |
+ // {[Record Source Type] [External Physical Address]}
+ // The first byte is used for record source type.
+ data[index++] = (byte) mSourceType;
+ }
+ extraParamToByteArray(data, index);
+ return getDataSize(includeType);
+ }
+ }
+
+ // ---------------------------------------------------------------------------------------------
+ // ---- Own source -----------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
+ /**
+ * Create {@link OwnSource} of own source.
+ */
+ public static OwnSource ofOwnSource() {
+ return new OwnSource();
+ }
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public static final class OwnSource extends RecordSource {
+ private static final int EXTRA_DATA_SIZE = 0;
+
+ private OwnSource() {
+ super(RECORD_SOURCE_TYPE_OWN_SOURCE, EXTRA_DATA_SIZE);
+ }
+
+ @Override
+ int extraParamToByteArray(byte[] data, int index) {
+ return 0;
+ }
+ }
+
+
+ // ---------------------------------------------------------------------------------------------
+ // ---- Digital service data -------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
+ /**
+ * Digital broadcast general types
+ */
+ /** @hide */
+ public static final int DIGITAL_BROADCAST_TYPE_ARIB = 0x0;
+ /** @hide */
+ public static final int DIGITAL_BROADCAST_TYPE_ATSC = 0x1;
+ /** @hide */
+ public static final int DIGITAL_BROADCAST_TYPE_DVB = 0x2;
+
+ /**
+ * Digital broadcast specific types
+ */
+ /** @hide */
+ public static final int DIGITAL_BROADCAST_TYPE_ARIB_BS = 0x8;
+ /** @hide */
+ public static final int DIGITAL_BROADCAST_TYPE_ARIB_CS = 0x9;
+ /** @hide */
+ public static final int DIGITAL_BROADCAST_TYPE_ARIB_T = 0xA;
+ /** @hide */
+ public static final int DIGITAL_BROADCAST_TYPE_ATSC_CABLE = 0x10;
+ /** @hide */
+ public static final int DIGITAL_BROADCAST_TYPE_ATSC_SATELLITE = 0x11;
+ /** @hide */
+ public static final int DIGITAL_BROADCAST_TYPE_ATSC_TERRESTRIAL = 0x12;
+ /** @hide */
+ public static final int DIGITAL_BROADCAST_TYPE_DVB_C = 0x18;
+ /** @hide */
+ public static final int DIGITAL_BROADCAST_TYPE_DVB_S = 0x19;
+ /** @hide */
+ public static final int DIGITAL_BROADCAST_TYPE_DVB_S2 = 0x1A;
+ /** @hide */
+ public static final int DIGITAL_BROADCAST_TYPE_DVB_T = 0x1B;
+
+ /** Channel number formats. */
+ private static final int CHANNEL_NUMBER_FORMAT_1_PART = 0x01;
+ private static final int CHANNEL_NUMBER_FORMAT_2_PART = 0x02;
+
+ /**
+ * Interface for digital source identification.
+ */
+ private interface DigitalServiceIdentification {
+ void toByteArray(byte[] data, int index);
+ }
+
+ /**
+ * Digital service identification for ARIB.
+ * <p>
+ * It consists of the following fields
+ * <ul>
+ * <li>transport stream id: 2bytes
+ * <li>service id: 2bytes
+ * <li>original network id: 2bytes
+ * </ul>
+ * @hide
+ */
+ public static final class AribData implements DigitalServiceIdentification {
+ /** The transport_stream_ID of the transport stream carrying the required service */
+ private final int mTransportStreamId;
+ /** The service_ID of the required service */
+ private final int mServiceId;
+ /**
+ * The original_network_ID of the network carrying the transport stream for the required
+ * service
+ */
+ private final int mOriginalNetworkId;
+
+ public AribData(int transportStreamId, int serviceId, int originalNetworkId) {
+ mTransportStreamId = transportStreamId;
+ mServiceId = serviceId;
+ mOriginalNetworkId = originalNetworkId;
+ }
+
+ @Override
+ public void toByteArray(byte[] data, int index) {
+ threeFieldsToSixBytes(mTransportStreamId, mServiceId, mOriginalNetworkId, data, index);
+ }
+ }
+
+ /**
+ * Digital service identification for ATSC.
+ * <p>
+ * It consists of the following fields
+ * <ul>
+ * <li>transport stream id: 2bytes
+ * <li>program number: 2bytes
+ * <li>reserved: 2bytes
+ * </ul>
+ * @hide
+ */
+ public static final class AtscData implements DigitalServiceIdentification {
+ /** The transport_stream_ID of the transport stream carrying the required service */
+ private final int mTransportStreamId;
+ /** The Program_number of the required service */
+ private final int mProgramNumber;
+
+ public AtscData(int transportStreamId, int programNumber) {
+ mTransportStreamId = transportStreamId;
+ mProgramNumber = programNumber;
+ }
+
+ @Override
+ public void toByteArray(byte[] data, int index) {
+ threeFieldsToSixBytes(mTransportStreamId, mProgramNumber, 0, data, index);
+ }
+ }
+
+ /**
+ * Digital service identification for DVB.
+ * <p>
+ * It consists of the following fields
+ * <ul>
+ * <li>transport stream id: 2bytes
+ * <li>service id: 2bytes
+ * <li>original network id: 2bytes
+ * </ul>
+ * @hide
+ */
+ public static final class DvbData implements DigitalServiceIdentification {
+ /** The transport_stream_ID of the transport stream carrying the required service */
+ private final int mTransportStreamId;
+ /** The service_ID of the required service */
+ private final int mServiceId;
+ /**
+ * The original_network_ID of the network carrying the transport stream for the required
+ * service
+ */
+ private final int mOriginalNetworkId;
+
+ public DvbData(int transportStreamId, int serviceId, int originalNetworkId) {
+ mTransportStreamId = transportStreamId;
+ mServiceId = serviceId;
+ mOriginalNetworkId = originalNetworkId;
+ }
+
+ @Override
+ public void toByteArray(byte[] data, int index) {
+ threeFieldsToSixBytes(mTransportStreamId, mServiceId, mOriginalNetworkId, data, index);
+ }
+ }
+
+ /**
+ * Identifies a 1-part Logical or Virtual Channel Number or a 2-part Major and Minor channel
+ * combination.
+ */
+ private static final class ChannelIdentifier {
+ /** Identifies Channel Format */
+ private final int mChannelNumberFormat;
+ /**
+ * Major Channel Number (if Channel Number Format is 2-part). If format is
+ * CHANNEL_NUMBER_FORMAT_1_PART, this will be ignored(0).
+ */
+ private final int mMajorChannelNumber;
+ /**
+ * 1-part Channel Number, or a Minor Channel Number (if Channel Number Format is 2-part).
+ */
+ private final int mMinorChannelNumber;
+
+ private ChannelIdentifier(int format, int majorNumber, int minorNumer) {
+ mChannelNumberFormat = format;
+ mMajorChannelNumber = majorNumber;
+ mMinorChannelNumber = minorNumer;
+ }
+
+ private void toByteArray(byte[] data, int index) {
+ // The first 6 bits for format, the 10 bits for major number.
+ data[index] = (byte) (((mChannelNumberFormat << 2) | (mMajorChannelNumber >>> 8) & 0x3));
+ data[index + 1] = (byte) (mMajorChannelNumber & 0xFF);
+ // Minor number uses the next 16 bits.
+ shortToByteArray((short) mMinorChannelNumber, data, index + 2);
+ }
+ }
+
+ /**
+ * Digital channel id.
+ * <p>
+ * It consists of the following fields
+ * <ul>
+ * <li>channel number format: 6bits
+ * <li>major number: 10bits
+ * <li>minor number: 16bits
+ * <li>reserved: 2bytes
+ * </ul>
+ * @hide
+ */
+ public static final class DigitalChannelData implements DigitalServiceIdentification {
+ /** Identifies the logical or virtual channel number of a service. */
+ private ChannelIdentifier mChannelIdentifier;
+
+ public static DigitalChannelData ofTwoNumbers(int majorNumber, int minorNumber) {
+ return new DigitalChannelData(
+ new ChannelIdentifier(CHANNEL_NUMBER_FORMAT_2_PART, majorNumber, minorNumber));
+ }
+
+ public static DigitalChannelData ofOneNumber(int number) {
+ return new DigitalChannelData(
+ new ChannelIdentifier(CHANNEL_NUMBER_FORMAT_1_PART, 0, number));
+ }
+
+ private DigitalChannelData(ChannelIdentifier id) {
+ mChannelIdentifier = id;
+ }
+
+ @Override
+ public void toByteArray(byte[] data, int index) {
+ mChannelIdentifier.toByteArray(data, index);
+ // The last 2 bytes is reserved for future use.
+ data[index + 4] = 0;
+ data[index + 5] = 0;
+ }
+ }
+
+ /**
+ * Create {@link DigitalServiceSource} with channel type.
+ *
+ * @param broadcastSystem digital broadcast system. It should be one of
+ * <ul>
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ARIB}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ATSC}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_DVB}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ARIB_BS}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ARIB_CS}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ARIB_T}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ATSC_CABLE}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ATSC_SATELLITE}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ATSC_TERRESTRIAL}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_DVB_C}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_DVB_S}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_DVB_S2}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_DVB_T}
+ * </ul>
+ * @hide
+ */
+ public static DigitalServiceSource ofDigitalChannelId(int broadcastSystem,
+ DigitalChannelData data) {
+ if (data == null) {
+ throw new IllegalArgumentException("data should not be null.");
+ }
+ switch (broadcastSystem) {
+ case DIGITAL_BROADCAST_TYPE_ARIB:
+ case DIGITAL_BROADCAST_TYPE_ATSC:
+ case DIGITAL_BROADCAST_TYPE_DVB:
+ case DIGITAL_BROADCAST_TYPE_ARIB_BS:
+ case DIGITAL_BROADCAST_TYPE_ARIB_CS:
+ case DIGITAL_BROADCAST_TYPE_ARIB_T:
+ case DIGITAL_BROADCAST_TYPE_ATSC_CABLE:
+ case DIGITAL_BROADCAST_TYPE_ATSC_SATELLITE:
+ case DIGITAL_BROADCAST_TYPE_ATSC_TERRESTRIAL:
+ case DIGITAL_BROADCAST_TYPE_DVB_C:
+ case DIGITAL_BROADCAST_TYPE_DVB_S:
+ case DIGITAL_BROADCAST_TYPE_DVB_S2:
+ case DIGITAL_BROADCAST_TYPE_DVB_T:
+ return new DigitalServiceSource(
+ DigitalServiceSource.DIGITAL_SERVICE_IDENTIFIED_BY_CHANNEL,
+ broadcastSystem,
+ data);
+ default:
+ Log.w(TAG, "Invalid broadcast type:" + broadcastSystem);
+ throw new IllegalArgumentException(
+ "Invalid broadcast system value:" + broadcastSystem);
+ }
+ }
+
+ /**
+ * Create {@link DigitalServiceSource} of ARIB type.
+ *
+ * @param aribType ARIB type. It should be one of
+ * <ul>
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ARIB}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ARIB_BS}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ARIB_CS}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ARIB_T}
+ * </ul>
+ * @hide
+ */
+ @Nullable
+ public static DigitalServiceSource ofArib(int aribType, AribData data) {
+ if (data == null) {
+ throw new IllegalArgumentException("data should not be null.");
+ }
+ switch (aribType) {
+ case DIGITAL_BROADCAST_TYPE_ARIB:
+ case DIGITAL_BROADCAST_TYPE_ARIB_BS:
+ case DIGITAL_BROADCAST_TYPE_ARIB_CS:
+ case DIGITAL_BROADCAST_TYPE_ARIB_T:
+ return new DigitalServiceSource(
+ DigitalServiceSource.DIGITAL_SERVICE_IDENTIFIED_BY_DIGITAL_ID,
+ aribType, data);
+ default:
+ Log.w(TAG, "Invalid ARIB type:" + aribType);
+ throw new IllegalArgumentException("type should not be null.");
+ }
+ }
+
+ /**
+ * Create {@link DigitalServiceSource} of ATSC type.
+ *
+ * @param atscType ATSC type. It should be one of
+ * <ul>
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ATSC}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ATSC_CABLE}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ATSC_SATELLITE}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_ATSC_TERRESTRIAL}
+ * </ul>
+ * @hide
+ */
+ @Nullable
+ public static DigitalServiceSource ofAtsc(int atscType, AtscData data) {
+ if (data == null) {
+ throw new IllegalArgumentException("data should not be null.");
+ }
+ switch (atscType) {
+ case DIGITAL_BROADCAST_TYPE_ATSC:
+ case DIGITAL_BROADCAST_TYPE_ATSC_CABLE:
+ case DIGITAL_BROADCAST_TYPE_ATSC_SATELLITE:
+ case DIGITAL_BROADCAST_TYPE_ATSC_TERRESTRIAL:
+ return new DigitalServiceSource(
+ DigitalServiceSource.DIGITAL_SERVICE_IDENTIFIED_BY_DIGITAL_ID,
+ atscType, data);
+ default:
+ Log.w(TAG, "Invalid ATSC type:" + atscType);
+ throw new IllegalArgumentException("Invalid ATSC type:" + atscType);
+ }
+ }
+
+ /**
+ * Create {@link DigitalServiceSource} of ATSC type.
+ *
+ * @param dvbType DVB type. It should be one of
+ * <ul>
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_DVB}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_DVB_C}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_DVB_S}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_DVB_S2}
+ * <li>{@link #DIGITAL_BROADCAST_TYPE_DVB_T}
+ * </ul>
+ * @hide
+ */
+ @Nullable
+ public static DigitalServiceSource ofDvb(int dvbType, DvbData data) {
+ if (data == null) {
+ throw new IllegalArgumentException("data should not be null.");
+ }
+ switch (dvbType) {
+ case DIGITAL_BROADCAST_TYPE_DVB:
+ case DIGITAL_BROADCAST_TYPE_DVB_C:
+ case DIGITAL_BROADCAST_TYPE_DVB_S:
+ case DIGITAL_BROADCAST_TYPE_DVB_S2:
+ case DIGITAL_BROADCAST_TYPE_DVB_T:
+ return new DigitalServiceSource(
+ DigitalServiceSource.DIGITAL_SERVICE_IDENTIFIED_BY_DIGITAL_ID,
+ dvbType, data);
+ default:
+ Log.w(TAG, "Invalid DVB type:" + dvbType);
+ throw new IllegalArgumentException("Invalid DVB type:" + dvbType);
+ }
+ }
+
+ /**
+ * Record source container for "Digital Service".
+ * <ul>
+ * <li>[Record Source Type] - 1 byte
+ * <li>[Digital Identification] - 7 bytes
+ * </ul>
+ * @hide
+ */
+ @SystemApi
+ public static final class DigitalServiceSource extends RecordSource {
+ /** Indicates that a service is identified by digital service IDs. */
+ private static final int DIGITAL_SERVICE_IDENTIFIED_BY_DIGITAL_ID = 0;
+ /** Indicates that a service is identified by a logical or virtual channel number. */
+ private static final int DIGITAL_SERVICE_IDENTIFIED_BY_CHANNEL = 1;
+
+ private static final int EXTRA_DATA_SIZE = 7;
+
+ /**
+ * Type of identification. It should be one of DIGITAL_SERVICE_IDENTIFIED_BY_DIGITAL_ID and
+ * DIGITAL_SERVICE_IDENTIFIED_BY_CHANNEL
+ */
+ private final int mIdentificationMethod;
+ /**
+ * Indicates the Digital Broadcast System of required service. This is present irrespective
+ * of the state of [Service Identification Method].
+ */
+ private final int mBroadcastSystem;
+
+ /**
+ * Extra parameter for digital service identification.
+ */
+ private final DigitalServiceIdentification mIdentification;
+
+ private DigitalServiceSource(int identificatinoMethod, int broadcastSystem,
+ DigitalServiceIdentification identification) {
+ super(RECORD_SOURCE_TYPE_DIGITAL_SERVICE, EXTRA_DATA_SIZE);
+ mIdentificationMethod = identificatinoMethod;
+ mBroadcastSystem = broadcastSystem;
+ mIdentification = identification;
+ }
+
+ @Override
+ int extraParamToByteArray(byte[] data, int index) {
+ data[index] = (byte) ((mIdentificationMethod << 7) | (mBroadcastSystem & 0x7F));
+ mIdentification.toByteArray(data, index + 1);
+ return EXTRA_DATA_SIZE;
+
+ }
+ }
+
+
+ // ---------------------------------------------------------------------------------------------
+ // ---- Analogue service data ------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
+ /**
+ * Analogue broadcast types.
+ */
+ /** @hide */
+ public static final int ANALOGUE_BROADCAST_TYPE_CABLE = 0x0;
+ /** @hide */
+ public static final int ANALOGUE_BROADCAST_TYPE_SATELLITE = 0x1;
+ /** @hide */
+ public static final int ANALOGUE_BROADCAST_TYPE_TERRESTRIAL = 0x2;
+
+ /**
+ * Broadcast system values.
+ */
+ /** @hide */
+ public static final int BROADCAST_SYSTEM_PAL_BG = 0;
+ /** @hide */
+ public static final int BROADCAST_SYSTEM_SECAM_LP = 1;
+ /** @hide */
+ public static final int BROADCAST_SYSTEM_PAL_M = 2;
+ /** @hide */
+ public static final int BROADCAST_SYSTEM_NTSC_M = 3;
+ /** @hide */
+ public static final int BROADCAST_SYSTEM_PAL_I = 4;
+ /** @hide */
+ public static final int BROADCAST_SYSTEM_SECAM_DK = 5;
+ /** @hide */
+ public static final int BROADCAST_SYSTEM_SECAM_BG = 6;
+ /** @hide */
+ public static final int BROADCAST_SYSTEM_SECAM_L = 7;
+ /** @hide */
+ public static final int BROADCAST_SYSTEM_PAL_DK = 8;
+ /** @hide */
+ public static final int BROADCAST_SYSTEM_PAL_OTHER_SYSTEM = 31;
+
+ /**
+ * Create {@link AnalogueServiceSource} of analogue service.
+ *
+ * @param broadcastType
+ * @param frequency
+ * @param broadcastSystem
+ * @hide
+ */
+ @Nullable
+ public static AnalogueServiceSource ofAnalogue(int broadcastType, int frequency,
+ int broadcastSystem){
+ if (broadcastType < ANALOGUE_BROADCAST_TYPE_CABLE
+ || broadcastType > ANALOGUE_BROADCAST_TYPE_TERRESTRIAL) {
+ Log.w(TAG, "Invalid Broadcast type:" + broadcastType);
+ throw new IllegalArgumentException("Invalid Broadcast type:" + broadcastType);
+ }
+ if (frequency < 0 || frequency > 0xFFFF) {
+ Log.w(TAG, "Invalid frequency value[0x0000-0xFFFF]:" + frequency);
+ throw new IllegalArgumentException(
+ "Invalid frequency value[0x0000-0xFFFF]:" + frequency);
+ }
+ if (broadcastSystem < BROADCAST_SYSTEM_PAL_BG
+ || broadcastSystem > BROADCAST_SYSTEM_PAL_OTHER_SYSTEM) {
+
+ Log.w(TAG, "Invalid Broadcast system:" + broadcastSystem);
+ throw new IllegalArgumentException(
+ "Invalid Broadcast system:" + broadcastSystem);
+ }
+
+ return new AnalogueServiceSource(broadcastType, frequency, broadcastSystem);
+ }
+
+ /**
+ * Record source for analogue service data. It consists of
+ * <ul>
+ * <li>[Record Source Type] - 1 byte
+ * <li>[Analogue Broadcast Type] - 1 byte
+ * <li>[Analogue Frequency] - 2 bytes
+ * <li>[Broadcast System] - 1 byte
+ * </ul>
+ * @hide
+ */
+ @SystemApi
+ public static final class AnalogueServiceSource extends RecordSource {
+ private static final int EXTRA_DATA_SIZE = 4;
+
+ /** Indicates the Analogue broadcast type. */
+ private final int mBroadcastType;
+ /** Used to specify the frequency used by an analogue tuner. 0x0000<N<0xFFFF. */
+ private final int mFrequency;
+ /**
+ * This specifies information about the color system, the sound carrier and the
+ * IF-frequency.
+ */
+ private final int mBroadcastSystem;
+
+ private AnalogueServiceSource(int broadcastType, int frequency, int broadcastSystem) {
+ super(RECORD_SOURCE_TYPE_ANALOGUE_SERVICE, EXTRA_DATA_SIZE);
+ mBroadcastType = broadcastType;
+ mFrequency = frequency;
+ mBroadcastSystem = broadcastSystem;
+ }
+
+ @Override
+ protected int extraParamToByteArray(byte[] data, int index) {
+ // [Analogue Broadcast Type] - 1 byte
+ data[index] = (byte) mBroadcastType;
+ // [Analogue Frequency] - 2 bytes
+ shortToByteArray((short) mFrequency, data, index + 1);
+ // [Broadcast System] - 1 byte
+ data[index + 3] = (byte) mBroadcastSystem;
+ return EXTRA_DATA_SIZE;
+ }
+ }
+
+
+ // ---------------------------------------------------------------------------------------------
+ // ---- External plug data ---------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
+ /**
+ * Create {@link ExternalPlugData} of external plug type.
+ *
+ * @param plugNumber plug number. It should be in range of [1, 255]
+ * @hide
+ */
+ public static ExternalPlugData ofExternalPlug(int plugNumber) {
+ if (plugNumber < 1 || plugNumber > 255) {
+ Log.w(TAG, "Invalid plug number[1-255]" + plugNumber);
+ throw new IllegalArgumentException("Invalid plug number[1-255]" + plugNumber);
+ }
+ return new ExternalPlugData(plugNumber);
+ }
+
+ /**
+ * Record source for external plug (external non-HDMI device connect) type.
+ * <ul>
+ * <li>[Record Source Type] - 1 byte
+ * <li>[External Plug] - 1 byte
+ * </ul>
+ * @hide
+ */
+ @SystemApi
+ public static final class ExternalPlugData extends RecordSource {
+ private static final int EXTRA_DATA_SIZE = 1;
+
+ /** External Plug number on the Recording Device. */
+ private final int mPlugNumber;
+
+ private ExternalPlugData(int plugNumber) {
+ super(RECORD_SOURCE_TYPE_EXTERNAL_PLUG, EXTRA_DATA_SIZE);
+ mPlugNumber = plugNumber;
+ }
+
+ @Override
+ int extraParamToByteArray(byte[] data, int index) {
+ data[index] = (byte) mPlugNumber;
+ return EXTRA_DATA_SIZE;
+ }
+ }
+
+ // ---------------------------------------------------------------------------------------------
+ // ---- External physical address --------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
+ /**
+ * Create {@link ExternalPhysicalAddress} of external physical address.
+ *
+ * @param physicalAddress
+ * @hide
+ */
+ public static ExternalPhysicalAddress ofExternalPhysicalAddress(int physicalAddress) {
+ if ((physicalAddress & ~0xFFFF) != 0) {
+ Log.w(TAG, "Invalid physical address:" + physicalAddress);
+ throw new IllegalArgumentException("Invalid physical address:" + physicalAddress);
+ }
+
+ return new ExternalPhysicalAddress(physicalAddress);
+ }
+
+ /**
+ * Record source for external physical address.
+ * <ul>
+ * <li>[Record Source Type] - 1 byte
+ * <li>[Physical address] - 2 byte
+ * </ul>
+ * @hide
+ */
+ @SystemApi
+ public static final class ExternalPhysicalAddress extends RecordSource {
+ private static final int EXTRA_DATA_SIZE = 2;
+
+ private final int mPhysicalAddress;
+
+ private ExternalPhysicalAddress(int physicalAddress) {
+ super(RECORD_SOURCE_TYPE_EXTERNAL_PHYSICAL_ADDRESS, EXTRA_DATA_SIZE);
+ mPhysicalAddress = physicalAddress;
+ }
+
+ @Override
+ int extraParamToByteArray(byte[] data, int index) {
+ shortToByteArray((short) mPhysicalAddress, data, index);
+ return EXTRA_DATA_SIZE;
+ }
+ }
+
+
+ // ---------------------------------------------------------------------------------------------
+ // ------- Helper methods ----------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
+ private static int threeFieldsToSixBytes(int first, int second, int third, byte[] data,
+ int index) {
+ shortToByteArray((short) first, data, index);
+ shortToByteArray((short) second, data, index + 2);
+ shortToByteArray((short) third, data, index + 4);
+ return 6;
+ }
+
+ private static int shortToByteArray(short value, byte[] byteArray, int index) {
+ byteArray[index] = (byte) ((value >>> 8) & 0xFF);
+ byteArray[index + 1] = (byte) (value & 0xFF);
+ return 2;
+ }
+
+ /**
+ * Check the byte array of record source.
+ * @hide
+ */
+ public static boolean checkRecordSource(byte[] recordSource) {
+ int recordSourceType = recordSource[0];
+ int extraDataSize = recordSource.length - 1;
+ switch (recordSourceType) {
+ case RECORD_SOURCE_TYPE_OWN_SOURCE:
+ return extraDataSize == OwnSource.EXTRA_DATA_SIZE;
+ case RECORD_SOURCE_TYPE_DIGITAL_SERVICE:
+ return extraDataSize == DigitalServiceSource.EXTRA_DATA_SIZE;
+ case RECORD_SOURCE_TYPE_ANALOGUE_SERVICE:
+ return extraDataSize == AnalogueServiceSource.EXTRA_DATA_SIZE;
+ case RECORD_SOURCE_TYPE_EXTERNAL_PLUG:
+ return extraDataSize == ExternalPlugData.EXTRA_DATA_SIZE;
+ case RECORD_SOURCE_TYPE_EXTERNAL_PHYSICAL_ADDRESS:
+ return extraDataSize == ExternalPhysicalAddress.EXTRA_DATA_SIZE;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java b/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java
new file mode 100644
index 0000000..3e5e49b
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.hdmi;
+
+import android.annotation.SystemApi;
+import android.hardware.hdmi.HdmiRecordSources.AnalogueServiceSource;
+import android.hardware.hdmi.HdmiRecordSources.DigitalServiceSource;
+import android.hardware.hdmi.HdmiRecordSources.ExternalPhysicalAddress;
+import android.hardware.hdmi.HdmiRecordSources.ExternalPlugData;
+import android.hardware.hdmi.HdmiRecordSources.RecordSource;
+import android.util.Log;
+
+/**
+ * Container for timer record source used for timer recording. Timer source consists of two parts,
+ * timer info and record source.
+ * <p>
+ * Timer info contains all timing information used for recording. It consists of the following
+ * values.
+ * <ul>
+ * <li>[Day of Month]
+ * <li>[Month of Year]
+ * <li>[Start Time]
+ * <li>[Duration]
+ * <li>[Recording Sequence]
+ * </ul>
+ * <p>
+ * Record source containers all program information used for recording.
+ * For more details, look at {@link HdmiRecordSources}.
+ * <p>
+ * Usage
+ * <pre>
+ * TimeOrDuration startTime = HdmiTimerRecordSources.ofTime(18, 00); // 6PM.
+ * TimeOrDuration duration = HdmiTimerRecordSource.ofDuration(1, 00); // 1 hour duration.
+ * // For 1 hour from 6PM, August 10th every SaturDay and Sunday.
+ * TimerInfo timerInfo = HdmiTimerRecordSource.timerInfoOf(10, 8, starTime, duration,
+ * HdmiTimerRecordSource.RECORDING_SEQUENCE_REPEAT_SATURDAY |
+ * HdmiTimerRecordSource.RECORDING_SEQUENCE_REPEAT_SUNDAY);
+ * // create digital source.
+ * DigitalServiceSource recordSource = HdmiRecordSource.ofDvb(...);
+ * TimerRecordSource source = ofDigitalSource(timerInfo, recordSource);
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+public class HdmiTimerRecordSources {
+ private static final String TAG = "HdmiTimerRecordingSources";
+
+ private HdmiTimerRecordSources() {}
+
+ /**
+ * Create {@link TimerRecordSource} for digital source which is used for <Set Digital
+ * Timer>.
+ *
+ * @param timerInfo timer info used for timer recording
+ * @param source digital source used for timer recording
+ * @return {@link TimerRecordSource}
+ * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null
+ */
+ public static TimerRecordSource ofDigitalSource(TimerInfo timerInfo,
+ DigitalServiceSource source) {
+ checkTimerRecordSourceInputs(timerInfo, source);
+ return new TimerRecordSource(timerInfo, source);
+ }
+
+ /**
+ * Create {@link TimerRecordSource} for analogue source which is used for <Set Analogue
+ * Timer>.
+ *
+ * @param timerInfo timer info used for timer recording
+ * @param source digital source used for timer recording
+ * @return {@link TimerRecordSource}
+ * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null
+ */
+ public static TimerRecordSource ofAnalogueSource(TimerInfo timerInfo,
+ AnalogueServiceSource source) {
+ checkTimerRecordSourceInputs(timerInfo, source);
+ return new TimerRecordSource(timerInfo, source);
+ }
+
+ /**
+ * Create {@link TimerRecordSource} for external plug which is used for <Set External
+ * Timer>.
+ *
+ * @param timerInfo timer info used for timer recording
+ * @param source digital source used for timer recording
+ * @return {@link TimerRecordSource}
+ * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null
+ */
+ public static TimerRecordSource ofExternalPlug(TimerInfo timerInfo, ExternalPlugData source) {
+ checkTimerRecordSourceInputs(timerInfo, source);
+ return new TimerRecordSource(timerInfo,
+ new ExternalSourceDecorator(source, EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG));
+ }
+
+ /**
+ * Create {@link TimerRecordSource} for external physical address which is used for <Set
+ * External Timer>.
+ *
+ * @param timerInfo timer info used for timer recording
+ * @param source digital source used for timer recording
+ * @return {@link TimerRecordSource}
+ * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null
+ */
+ public static TimerRecordSource ofExternalPhysicalAddress(TimerInfo timerInfo,
+ ExternalPhysicalAddress source) {
+ checkTimerRecordSourceInputs(timerInfo, source);
+ return new TimerRecordSource(timerInfo,
+ new ExternalSourceDecorator(source,
+ EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS));
+ }
+
+ private static void checkTimerRecordSourceInputs(TimerInfo timerInfo, RecordSource source) {
+ if (timerInfo == null) {
+ Log.w(TAG, "TimerInfo should not be null.");
+ throw new IllegalArgumentException("TimerInfo should not be null.");
+ }
+ if (source == null) {
+ Log.w(TAG, "source should not be null.");
+ throw new IllegalArgumentException("source should not be null.");
+ }
+ }
+
+ /**
+ * Create {@link Duration} for time value.
+ *
+ * @param hour hour in range of [0, 24]
+ * @param minute minute in range of [0, 60]
+ * @return {@link Duration}
+ * @throws IllegalArgumentException if hour or minute is out of range
+ */
+ public static Time ofTime(int hour, int minute) {
+ checkTimeValue(hour, minute);
+ return new Time(hour, minute);
+ }
+
+ private static void checkTimeValue(int hour, int minute) {
+ if (hour < 0 || hour > 24) {
+ throw new IllegalArgumentException("Hour should be in rage of [0, 24]:" + hour);
+ }
+ if (minute < 0 || minute > 60) {
+ throw new IllegalArgumentException("Minute should be in rage of [0, 60]:" + minute);
+ }
+ }
+
+ /**
+ * Create {@link Duration} for duration value.
+ *
+ * @param hour hour in range of [0, 90]
+ * @param minute minute in range of [0, 60]
+ * @return {@link Duration}
+ * @throws IllegalArgumentException if hour or minute is out of range
+ */
+ public static Duration ofDuration(int hour, int minute) {
+ checkDurationValue(hour, minute);
+ return new Duration(hour, minute);
+ }
+
+ private static void checkDurationValue(int hour, int minute) {
+ if (hour < 0 || hour > 99) {
+ throw new IllegalArgumentException("Hour should be in rage of [0, 99]:" + hour);
+ }
+ if (minute < 0 || minute > 60) {
+ throw new IllegalArgumentException("minute should be in rage of [0, 60]:" + minute);
+ }
+ }
+
+ private static class TimeUnit {
+ protected final int mHour;
+ protected final int mMinute;
+
+ protected TimeUnit(int hour, int minute) {
+ mHour = hour;
+ mMinute = minute;
+ }
+
+ protected int toByteArray(byte[] data, int index) {
+ data[index] = toBcdByte(mHour);
+ data[index + 1] = toBcdByte(mMinute);
+ return 2;
+ }
+
+ protected static byte toBcdByte(int value) {
+ int digitOfTen = (value / 10) % 10;
+ int digitOfOne = value % 10;
+ return (byte) ((digitOfTen << 4) | digitOfOne);
+ }
+ }
+
+ /**
+ * Place holder for time value.
+ * @hide
+ */
+ @SystemApi
+ public static class Time extends TimeUnit {
+ private Time(int hour, int minute) {
+ super(hour, minute);
+ }
+ }
+
+ /**
+ * Place holder for duration value.
+ * @hide
+ */
+ @SystemApi
+ public static class Duration extends TimeUnit {
+ private Duration(int hour, int minute) {
+ super(hour, minute);
+ }
+ }
+
+ /**
+ * Fields for recording sequence.
+ * The following can be merged by OR(|) operation.
+ */
+ public static final int RECORDING_SEQUENCE_REPEAT_ONCE_ONLY = 0;
+ public static final int RECORDING_SEQUENCE_REPEAT_SUNDAY = 1 << 0;
+ public static final int RECORDING_SEQUENCE_REPEAT_MONDAY = 1 << 1;
+ public static final int RECORDING_SEQUENCE_REPEAT_TUESDAY = 1 << 2;
+ public static final int RECORDING_SEQUENCE_REPEAT_WEDNESDAY = 1 << 3;
+ public static final int RECORDING_SEQUENCE_REPEAT_THURSDAY = 1 << 4;
+ public static final int RECORDING_SEQUENCE_REPEAT_FRIDAY = 1 << 5;
+ public static final int RECORDING_SEQUENCE_REPEAT_SATUREDAY = 1 << 6;
+
+ private static final int RECORDING_SEQUENCE_REPEAT_MASK =
+ (RECORDING_SEQUENCE_REPEAT_SUNDAY | RECORDING_SEQUENCE_REPEAT_MONDAY |
+ RECORDING_SEQUENCE_REPEAT_TUESDAY | RECORDING_SEQUENCE_REPEAT_WEDNESDAY |
+ RECORDING_SEQUENCE_REPEAT_THURSDAY | RECORDING_SEQUENCE_REPEAT_FRIDAY |
+ RECORDING_SEQUENCE_REPEAT_SATUREDAY);
+
+ /**
+ * Create {@link TimerInfo} with the given information.
+ *
+ * @param dayOfMonth day of month
+ * @param monthOfYear month of year
+ * @param startTime start time in {@link Time}
+ * @param duration duration in {@link Duration}
+ * @param recordingSequence recording sequence. Use RECORDING_SEQUENCE_REPEAT_ONCE_ONLY for no
+ * repeat. Otherwise use combination of {@link #RECORDING_SEQUENCE_REPEAT_SUNDAY},
+ * {@link #RECORDING_SEQUENCE_REPEAT_MONDAY},
+ * {@link #RECORDING_SEQUENCE_REPEAT_TUESDAY},
+ * {@link #RECORDING_SEQUENCE_REPEAT_WEDNESDAY},
+ * {@link #RECORDING_SEQUENCE_REPEAT_THURSDAY},
+ * {@link #RECORDING_SEQUENCE_REPEAT_FRIDAY},
+ * {@link #RECORDING_SEQUENCE_REPEAT_SATUREDAY}.
+ * @return {@link TimerInfo}.
+ * @throws IllegalArgumentException if input value is invalid
+ */
+ public static TimerInfo timerInfoOf(int dayOfMonth, int monthOfYear, Time startTime,
+ Duration duration, int recordingSequence) {
+ if (dayOfMonth < 0 || dayOfMonth > 31) {
+ throw new IllegalArgumentException(
+ "Day of month should be in range of [0, 31]:" + dayOfMonth);
+ }
+ if (monthOfYear < 1 || monthOfYear > 12) {
+ throw new IllegalArgumentException(
+ "Month of year should be in range of [1, 12]:" + monthOfYear);
+ }
+ checkTimeValue(startTime.mHour, startTime.mMinute);
+ checkDurationValue(duration.mHour, duration.mMinute);
+ // Recording sequence should use least 7 bits or no bits.
+ if ((recordingSequence != 0)
+ && ((recordingSequence & ~RECORDING_SEQUENCE_REPEAT_MASK) != 0)) {
+ throw new IllegalArgumentException(
+ "Invalid reecording sequence value:" + recordingSequence);
+ }
+
+ return new TimerInfo(dayOfMonth, monthOfYear, startTime, duration, recordingSequence);
+ }
+
+ /**
+ * Container basic timer information. It consists of the following fields.
+ * <ul>
+ * <li>[Day of Month]
+ * <li>[Month of Year]
+ * <li>[Start Time]
+ * <li>[Duration]
+ * <li>[Recording Sequence]
+ * </ul>
+ * @hide
+ */
+ @SystemApi
+ public static class TimerInfo {
+ private static final int DAY_OF_MONTH_SIZE = 1;
+ private static final int MONTH_OF_YEAR_SIZE = 1;
+ private static final int START_TIME_SIZE = 2; // 1byte for hour and 1byte for minute.
+ private static final int DURATION_SIZE = 2; // 1byte for hour and 1byte for minute.
+ private static final int RECORDING_SEQUENCE_SIZE = 1;
+ private static final int BASIC_INFO_SIZE = DAY_OF_MONTH_SIZE + MONTH_OF_YEAR_SIZE
+ + START_TIME_SIZE + DURATION_SIZE + RECORDING_SEQUENCE_SIZE;
+
+ /** Day of month. */
+ private final int mDayOfMonth;
+ /** Month of year. */
+ private final int mMonthOfYear;
+ /**
+ * Time of day.
+ * [Hour][Minute]. 0 <= Hour <= 24, 0 <= Minute <= 60 in BCD format.
+ */
+ private final Time mStartTime;
+ /**
+ * Duration. [Hour][Minute].
+ * 0 <= Hour <= 99, 0 <= Minute <= 60 in BCD format.
+ * */
+ private final Duration mDuration;
+ /**
+ * Indicates if recording is repeated and, if so, on which days. For repeated recordings,
+ * the recording sequence value is the bitwise OR of the days when recordings are required.
+ * [Recording Sequence] shall be set to 0x00 when the recording is not repeated. Bit 7 is
+ * reserved and shall be set to zero.
+ */
+ private final int mRecordingSequence;
+
+ private TimerInfo(int dayOfMonth, int monthOfYear, Time startTime,
+ Duration duration, int recordingSequence) {
+ mDayOfMonth = dayOfMonth;
+ mMonthOfYear = monthOfYear;
+ mStartTime = startTime;
+ mDuration = duration;
+ mRecordingSequence = recordingSequence;
+ }
+
+ int toByteArray(byte[] data, int index) {
+ // [Day of Month]
+ data[index] = (byte) mDayOfMonth;
+ index += DAY_OF_MONTH_SIZE;
+ // [Month of Year]
+ data[index] = (byte) mMonthOfYear;
+ index += MONTH_OF_YEAR_SIZE;
+ // [Start Time]
+ index += mStartTime.toByteArray(data, index);
+ index += mDuration.toByteArray(data, index);
+ // [Duration]
+ // [Recording Sequence]
+ data[index] = (byte) mRecordingSequence;
+ return getDataSize();
+ }
+
+ int getDataSize() {
+ return BASIC_INFO_SIZE;
+ }
+ }
+
+ /**
+ * Record source container for timer record. This is used to set parameter for <Set Digital
+ * Timer>, <Set Analogue Timer>, and <Set External Timer> message.
+ * <p>
+ * In order to create this from each source type, use one of helper method.
+ * <ul>
+ * <li>{@link #ofDigitalSource} for digital source
+ * <li>{@link #ofAnalogueSource} for analogue source
+ * <li>{@link #ofExternalPlug} for external plug type
+ * <li>{@link #ofExternalPhysicalAddress} for external physical address type.
+ * </ul>
+ * @hide
+ */
+ @SystemApi
+ public static class TimerRecordSource {
+ private final RecordSource mRecordSource;
+ private final TimerInfo mTimerInfo;
+
+ private TimerRecordSource(TimerInfo timerInfo, RecordSource recordSource) {
+ mTimerInfo = timerInfo;
+ mRecordSource = recordSource;
+ }
+
+ int getDataSize() {
+ return mTimerInfo.getDataSize() + mRecordSource.getDataSize(false);
+ }
+
+ int toByteArray(byte[] data, int index) {
+ // Basic infos including [Day of Month] [Month of Year] [Start Time] [Duration]
+ // [Recording Sequence]
+ index += mTimerInfo.toByteArray(data, index);
+ // [Record Source]
+ mRecordSource.toByteArray(false, data, index);
+ return getDataSize();
+ }
+ }
+
+ /**
+ * External source specifier types.
+ */
+ private static final int EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG = 4;
+ private static final int EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS = 5;
+
+ /**
+ * Decorator for external source. Beside digital or analogue source, external source starts with
+ * [External Source Specifier] because it covers both external plug type and external specifier.
+ */
+ private static class ExternalSourceDecorator extends RecordSource {
+ private final RecordSource mRecordSource;
+ private final int mExternalSourceSpecifier;
+
+ private ExternalSourceDecorator(RecordSource recordSource, int externalSourceSpecifier) {
+ // External source has one byte field for [External Source Specifier].
+ super(recordSource.mSourceType, recordSource.getDataSize(false) + 1);
+ mRecordSource = recordSource;
+ mExternalSourceSpecifier = externalSourceSpecifier;
+ }
+
+ @Override
+ int extraParamToByteArray(byte[] data, int index) {
+ data[index] = (byte) mExternalSourceSpecifier;
+ mRecordSource.toByteArray(false, data, index + 1);
+ return getDataSize(false);
+ }
+ }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index 1285ccb..6080914 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -15,12 +15,15 @@
*/
package android.hardware.hdmi;
+import static android.hardware.hdmi.HdmiRecordSources.RecordSource;
+import static android.hardware.hdmi.HdmiTimerRecordSources.TimerRecordSource;
+
import android.annotation.SystemApi;
-import android.hardware.hdmi.HdmiControlManager.VendorCommandListener;
-import android.hardware.hdmi.IHdmiVendorCommandListener;
import android.os.RemoteException;
import android.util.Log;
+import libcore.util.EmptyArray;
+
/**
* HdmiTvClient represents HDMI-CEC logical device of type TV in the Android system
* which acts as TV/Display. It provides with methods that manage, interact with other
@@ -91,6 +94,7 @@
return new HdmiTvClient(service);
}
+ @Override
public int getDeviceType() {
return HdmiCecDeviceInfo.DEVICE_TV;
}
@@ -150,6 +154,125 @@
}
}
+ /**
+ * Callback interface to used to get notified when a record request from recorder device.
+ */
+ public interface RecordRequestListener {
+ /**
+ * Called when tv receives request request from recorder device. When it's called,
+ * it should return record source in byte array so that hdmi control service
+ * can start recording with the given source info.
+ *
+ * @return {@link HdmiRecordSources} to be used to set recording info
+ */
+ RecordSource onRecordRequestReceived(int recorderAddress);
+ }
+
+ /**
+ * Set {@link RecordRequestListener} to hdmi control service.
+ */
+ public void setOneTouchRecordRequestListener(RecordRequestListener listener) {
+ try {
+ mService.setOneTouchRecordRequestListener(getCallbackWrapper(listener));
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to set record request listener: ", e);
+ }
+ }
+
+ /**
+ * Start one touch recording with the given recorder address and recorder source.
+ * <p>
+ * Usage
+ * <pre>
+ * HdmiTvClient tvClient = ....;
+ * // for own source.
+ * OwnSource ownSource = ownHdmiRecordSources.ownSource();
+ * tvClient.startOneTouchRecord(recorderAddress, ownSource);
+ * </pre>
+ */
+ public void startOneTouchRecord(int recorderAddress, RecordSource source) {
+ try {
+ byte[] data = new byte[source.getDataSize(true)];
+ source.toByteArray(true, data, 0);
+ mService.startOneTouchRecord(recorderAddress, data);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to start record: ", e);
+ }
+ }
+
+ /**
+ * Stop one touch record.
+ *
+ * @param recorderAddress recorder address where recoding will be stopped
+ */
+ public void stopOneTouchRecord(int recorderAddress) {
+ try {
+ mService.stopOneTouchRecord(recorderAddress);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to stop record: ", e);
+ }
+ }
+
+ /**
+ * Start timer recording with the given recoder address and recorder source.
+ * <p>
+ * Usage
+ * <pre>
+ * HdmiTvClient tvClient = ....;
+ * // create timer info
+ * TimerInfo timerInfo = HdmiTimerRecourdSources.timerInfoOf(...);
+ * // for digital source.
+ * DigitalServiceSource recordSource = HdmiRecordSources.ofDigitalService(...);
+ * // create timer recording source.
+ * TimerRecordSource source = HdmiTimerRecourdSources.ofDigitalSource(timerInfo, recordSource);
+ * tvClient.startTimerRecording(recorderAddress, source);
+ * </pre>
+ *
+ * @param recorderAddress target recorder address
+ * @param sourceType type of record source. It should be one of
+ * {@link HdmiControlManager#TIMER_RECORDING_TYPE_DIGITAL},
+ * {@link HdmiControlManager#TIMER_RECORDING_TYPE_ANALOGUE},
+ * {@link HdmiControlManager#TIMER_RECORDING_TYPE_EXTERNAL}.
+ * @param source record source to be used
+ */
+ public void startTimerRecording(int recorderAddress, int sourceType, TimerRecordSource source) {
+ checkTimerRecordingSourceType(sourceType);
+
+ try {
+ byte[] data = new byte[source.getDataSize()];
+ source.toByteArray(data, 0);
+ mService.startTimerRecording(recorderAddress, sourceType, data);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to start record: ", e);
+ }
+ }
+
+ private void checkTimerRecordingSourceType(int sourceType) {
+ switch (sourceType) {
+ case HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL:
+ case HdmiControlManager.TIMER_RECORDING_TYPE_ANALOGUE:
+ case HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL:
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid source type:" + sourceType);
+ }
+ }
+
+ /**
+ * Clear timer recording with the given recorder address and recording source.
+ * For more details, please refer {@link #startTimerRecording(int, int, TimerRecordSource)}.
+ */
+ public void clearTimerRecording(int recorderAddress, int sourceType, TimerRecordSource source) {
+ checkTimerRecordingSourceType(sourceType);
+ try {
+ byte[] data = new byte[source.getDataSize()];
+ source.toByteArray(data, 0);
+ mService.clearTimerRecording(recorderAddress, sourceType, data);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to start record: ", e);
+ }
+ }
+
private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) {
return new IHdmiControlCallback.Stub() {
@Override
@@ -158,4 +281,21 @@
}
};
}
+
+ private static IHdmiRecordRequestListener getCallbackWrapper(
+ final RecordRequestListener listener) {
+ return new IHdmiRecordRequestListener.Stub() {
+ @Override
+ public byte[] onRecordRequestReceived(int recorderAddress) throws RemoteException {
+ HdmiRecordSources.RecordSource source =
+ listener.onRecordRequestReceived(recorderAddress);
+ if (source == null) {
+ return EmptyArray.BYTE;
+ }
+ byte[] data = new byte[source.getDataSize(true)];
+ source.toByteArray(true, data, 0);
+ return data;
+ }
+ };
+ }
}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index f3931e3..95e0ee0 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -22,6 +22,7 @@
import android.hardware.hdmi.IHdmiDeviceEventListener;
import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.hardware.hdmi.IHdmiInputChangeListener;
+import android.hardware.hdmi.IHdmiRecordRequestListener;
import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
import android.hardware.hdmi.IHdmiVendorCommandListener;
@@ -54,11 +55,16 @@
void setArcMode(boolean enabled);
void setOption(int option, int value);
void setProhibitMode(boolean enabled);
- oneway void setSystemAudioVolume(int oldIndex, int newIndex, int maxIndex);
- oneway void setSystemAudioMute(boolean mute);
+ void setSystemAudioVolume(int oldIndex, int newIndex, int maxIndex);
+ void setSystemAudioMute(boolean mute);
void setInputChangeListener(IHdmiInputChangeListener listener);
List<HdmiCecDeviceInfo> getInputDevices();
void sendVendorCommand(int deviceType, int targetAddress, in byte[] params,
boolean hasVendorId);
void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType);
+ void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener);
+ void startOneTouchRecord(int recorderAddress, in byte[] recordSource);
+ void stopOneTouchRecord(int recorderAddress);
+ void startTimerRecording(int recorderAddress, int sourceType, in byte[] recordSource);
+ void clearTimerRecording(int recorderAddress, int sourceType, in byte[] recordSource);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TetheringController.java b/core/java/android/hardware/hdmi/IHdmiRecordRequestListener.aidl
similarity index 65%
copy from packages/SystemUI/src/com/android/systemui/statusbar/policy/TetheringController.java
copy to core/java/android/hardware/hdmi/IHdmiRecordRequestListener.aidl
index 143ebaa..f8f9e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TetheringController.java
+++ b/core/java/android/hardware/hdmi/IHdmiRecordRequestListener.aidl
@@ -14,15 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package android.hardware.hdmi;
-public interface TetheringController {
- void addCallback(Callback callback);
- void removeCallback(Callback callback);
- boolean isHotspotEnabled();
- boolean isHotspotSupported();
-
- public interface Callback {
- void onHotspotChanged(boolean hotspot);
- }
-}
+/**
+ * Callback interface definition for HDMI client to fill record source info
+ * when it gets record start request.
+ *
+ * @hide
+ */
+interface IHdmiRecordRequestListener {
+ byte[] onRecordRequestReceived(int recorderAddress);
+}
\ No newline at end of file
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 8de545e..0df4d45 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -543,8 +543,6 @@
private final IConnectivityManager mService;
- private final String mPackageName;
-
private INetworkManagementService mNMService;
/**
@@ -646,23 +644,6 @@
}
/**
- * Checks if the given network type should be exempt from VPN routing rules
- *
- * @hide
- */
- public static boolean isNetworkTypeExempt(int networkType) {
- switch (networkType) {
- case TYPE_MOBILE_MMS:
- case TYPE_MOBILE_SUPL:
- case TYPE_MOBILE_HIPRI:
- case TYPE_MOBILE_IA:
- return true;
- default:
- return false;
- }
- }
-
- /**
* Specifies the preferred network type. When the device has more
* than one type available the preferred network type will be used.
*
@@ -1103,6 +1084,65 @@
return null;
}
+ private int inferLegacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
+ if (netCap == null) {
+ return TYPE_NONE;
+ }
+ if (!netCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+ return TYPE_NONE;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
+ if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableCBS"))) {
+ return TYPE_MOBILE_CBS;
+ } else {
+ return TYPE_NONE;
+ }
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
+ if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableIMS"))) {
+ return TYPE_MOBILE_IMS;
+ } else {
+ return TYPE_NONE;
+ }
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
+ if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableFOTA"))) {
+ return TYPE_MOBILE_FOTA;
+ } else {
+ return TYPE_NONE;
+ }
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
+ if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableDUN"))) {
+ return TYPE_MOBILE_DUN;
+ } else {
+ return TYPE_NONE;
+ }
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
+ if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableSUPL"))) {
+ return TYPE_MOBILE_SUPL;
+ } else {
+ return TYPE_NONE;
+ }
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
+ if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableMMS"))) {
+ return TYPE_MOBILE_MMS;
+ } else {
+ return TYPE_NONE;
+ }
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableHIPRI"))) {
+ return TYPE_MOBILE_HIPRI;
+ } else {
+ return TYPE_NONE;
+ }
+ }
+ return TYPE_NONE;
+ }
+
private int legacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
if (netCap == null) return TYPE_NONE;
if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
@@ -1156,7 +1196,7 @@
};
}
- private HashMap<NetworkCapabilities, LegacyRequest> sLegacyRequests =
+ private static HashMap<NetworkCapabilities, LegacyRequest> sLegacyRequests =
new HashMap<NetworkCapabilities, LegacyRequest>();
private NetworkRequest findRequestForFeature(NetworkCapabilities netCap) {
@@ -1236,13 +1276,7 @@
* {@link #setProcessDefaultNetwork} and {@link Network#getSocketFactory} api.
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
- InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress);
-
- if (inetAddress == null) {
- return false;
- }
-
- return requestRouteToHostAddress(networkType, inetAddress);
+ return requestRouteToHostAddress(networkType, NetworkUtils.intToInetAddress(hostAddress));
}
/**
@@ -1260,9 +1294,8 @@
* {@link #setProcessDefaultNetwork} api.
*/
public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
- byte[] address = hostAddress.getAddress();
try {
- return mService.requestRouteToHostAddress(networkType, address, mPackageName);
+ return mService.requestRouteToHostAddress(networkType, hostAddress.getAddress());
} catch (RemoteException e) {
return false;
}
@@ -1433,9 +1466,8 @@
/**
* {@hide}
*/
- public ConnectivityManager(IConnectivityManager service, String packageName) {
+ public ConnectivityManager(IConnectivityManager service) {
mService = checkNotNull(service, "missing IConnectivityManager");
- mPackageName = checkNotNull(packageName, "missing package name");
}
/** {@hide} */
@@ -1721,7 +1753,7 @@
/**
* Report a problem network to the framework. This provides a hint to the system
- * that there might be connectivity problems on this network and may cause
+ * that there might be connectivity problems on this network and may cause
* the framework to re-evaluate network connectivity and/or switch to another
* network.
*
@@ -2371,7 +2403,7 @@
*/
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) {
sendRequestForNetwork(request.networkCapabilities, networkCallback, 0,
- REQUEST, TYPE_NONE);
+ REQUEST, inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));
}
/**
@@ -2393,7 +2425,7 @@
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
int timeoutMs) {
sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs,
- REQUEST, TYPE_NONE);
+ REQUEST, inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));
}
/**
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index f61984a..8b12fb8 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -69,9 +69,7 @@
int stopUsingNetworkFeature(int networkType, in String feature);
- boolean requestRouteToHost(int networkType, int hostAddress, String packageName);
-
- boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress, String packageName);
+ boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
/** Policy control over specific {@link NetworkStateTracker}. */
void setPolicyDataEnable(int networkType, boolean enabled);
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 9b95305..663aa15 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -36,12 +36,6 @@
private static final String TAG = "NetworkUtils";
- /** Bring the named network interface up. */
- public native static int enableInterface(String interfaceName);
-
- /** Bring the named network interface down. */
- public native static int disableInterface(String interfaceName);
-
/** Setting bit 0 indicates reseting of IPv4 addresses required */
public static final int RESET_IPV4_ADDRESSES = 0x01;
diff --git a/core/java/android/net/PSKKeyManager.java b/core/java/android/net/PskKeyManager.java
similarity index 77%
rename from core/java/android/net/PSKKeyManager.java
rename to core/java/android/net/PskKeyManager.java
index e868c4f..d162282 100644
--- a/core/java/android/net/PSKKeyManager.java
+++ b/core/java/android/net/PskKeyManager.java
@@ -16,6 +16,7 @@
package android.net;
+import com.android.org.conscrypt.PSKKeyManager;
import java.net.Socket;
import javax.crypto.SecretKey;
import javax.net.ssl.SSLEngine;
@@ -40,10 +41,10 @@
*
* <h3>Supporting multiple keys</h3>
*
- * <p>A peer may have multiple keys to choose from. To help choose the right key, during the handshake
- * the server can provide a <em>PSK identity hint</em> to the client, and the client can provide a
- * <em>PSK identity</em> to the server. The contents of these two pieces of information are specific
- * to application-level protocols.</p>
+ * <p>A peer may have multiple keys to choose from. To help choose the right key, during the
+ * handshake the server can provide a <em>PSK identity hint</em> to the client, and the client can
+ * provide a <em>PSK identity</em> to the server. The contents of these two pieces of information
+ * are specific to application-level protocols.</p>
*
* <p><em>NOTE: Both the PSK identity hint and the PSK identity are transmitted in cleartext.
* Moreover, these data are received and processed prior to peer having been authenticated. Thus,
@@ -76,16 +77,20 @@
* {@link #MAX_IDENTITY_LENGTH_BYTES} and {@link #MAX_IDENTITY_HINT_LENGTH_BYTES}).</li>
* </ul></p>
*
+ * <h3>Subclassing</h3>
+ * Subclasses should normally provide their own implementation of {@code getKey} because the default
+ * implementation returns no key, which aborts the handshake.
+ *
* <h3>Example</h3>
* The following example illustrates how to create an {@code SSLContext} which enables the use of
* TLS-PSK in {@code SSLSocket}, {@code SSLServerSocket} and {@code SSLEngine} instances obtained
* from it.
* <pre> {@code
- * PSKKeyManager myPskKeyManager = ...;
+ * PskKeyManager pskKeyManager = ...;
*
* SSLContext sslContext = SSLContext.getInstance("TLS");
* sslContext.init(
- * new KeyManager[] {myPskKeyManager},
+ * new KeyManager[] {pskKeyManager},
* new TrustManager[0], // No TrustManagers needed for TLS-PSK
* null // Use the default source of entropy
* );
@@ -93,7 +98,7 @@
* SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(...);
* }</pre>
*/
-public interface PSKKeyManager extends com.android.org.conscrypt.PSKKeyManager {
+public abstract class PskKeyManager implements PSKKeyManager {
// IMPLEMENTATION DETAILS: This class exists only because the default implemenetation of the
// TLS/SSL JSSE provider (currently Conscrypt) cannot depend on Android framework classes.
// As a result, this framework class simply extends the PSKKeyManager interface from Conscrypt
@@ -103,61 +108,83 @@
/**
* Maximum supported length (in bytes) for PSK identity hint (in modified UTF-8 representation).
*/
- int MAX_IDENTITY_HINT_LENGTH_BYTES =
- com.android.org.conscrypt.PSKKeyManager.MAX_IDENTITY_HINT_LENGTH_BYTES;
+ public static final int MAX_IDENTITY_HINT_LENGTH_BYTES =
+ PSKKeyManager.MAX_IDENTITY_HINT_LENGTH_BYTES;
/** Maximum supported length (in bytes) for PSK identity (in modified UTF-8 representation). */
- int MAX_IDENTITY_LENGTH_BYTES =
- com.android.org.conscrypt.PSKKeyManager.MAX_IDENTITY_LENGTH_BYTES;
+ public static final int MAX_IDENTITY_LENGTH_BYTES = PSKKeyManager.MAX_IDENTITY_LENGTH_BYTES;
/** Maximum supported length (in bytes) for PSK. */
- int MAX_KEY_LENGTH_BYTES = com.android.org.conscrypt.PSKKeyManager.MAX_KEY_LENGTH_BYTES;
+ public static final int MAX_KEY_LENGTH_BYTES = PSKKeyManager.MAX_KEY_LENGTH_BYTES;
/**
* Gets the PSK identity hint to report to the client to help agree on the PSK for the provided
* socket.
*
+ * <p>
+ * The default implementation returns {@code null}.
+ *
* @return PSK identity hint to be provided to the client or {@code null} to provide no hint.
*/
@Override
- String chooseServerKeyIdentityHint(Socket socket);
+ public String chooseServerKeyIdentityHint(Socket socket) {
+ return null;
+ }
/**
* Gets the PSK identity hint to report to the client to help agree on the PSK for the provided
* engine.
*
+ * <p>
+ * The default implementation returns {@code null}.
+ *
* @return PSK identity hint to be provided to the client or {@code null} to provide no hint.
*/
@Override
- String chooseServerKeyIdentityHint(SSLEngine engine);
+ public String chooseServerKeyIdentityHint(SSLEngine engine) {
+ return null;
+ }
/**
* Gets the PSK identity to report to the server to help agree on the PSK for the provided
* socket.
*
+ * <p>
+ * The default implementation returns an empty string.
+ *
* @param identityHint identity hint provided by the server or {@code null} if none provided.
*
* @return PSK identity to provide to the server. {@code null} is permitted but will be
* converted into an empty string.
*/
@Override
- String chooseClientKeyIdentity(String identityHint, Socket socket);
+ public String chooseClientKeyIdentity(String identityHint, Socket socket) {
+ return "";
+ }
/**
* Gets the PSK identity to report to the server to help agree on the PSK for the provided
* engine.
*
+ * <p>
+ * The default implementation returns an empty string.
+ *
* @param identityHint identity hint provided by the server or {@code null} if none provided.
*
* @return PSK identity to provide to the server. {@code null} is permitted but will be
* converted into an empty string.
*/
@Override
- String chooseClientKeyIdentity(String identityHint, SSLEngine engine);
+ public String chooseClientKeyIdentity(String identityHint, SSLEngine engine) {
+ return "";
+ }
/**
* Gets the PSK to use for the provided socket.
*
+ * <p>
+ * The default implementation returns {@code null}.
+ *
* @param identityHint identity hint provided by the server to help select the key or
* {@code null} if none provided.
* @param identity identity provided by the client to help select the key.
@@ -166,11 +193,16 @@
* the handshake.
*/
@Override
- SecretKey getKey(String identityHint, String identity, Socket socket);
+ public SecretKey getKey(String identityHint, String identity, Socket socket) {
+ return null;
+ }
/**
* Gets the PSK to use for the provided engine.
*
+ * <p>
+ * The default implementation returns {@code null}.
+ *
* @param identityHint identity hint provided by the server to help select the key or
* {@code null} if none provided.
* @param identity identity provided by the client to help select the key.
@@ -179,5 +211,7 @@
* the handshake.
*/
@Override
- SecretKey getKey(String identityHint, String identity, SSLEngine engine);
+ public SecretKey getKey(String identityHint, String identity, SSLEngine engine) {
+ return null;
+ }
}
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 0967fa8..3fd415f 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -577,7 +577,7 @@
* @return this {@link Builder} object to facilitate chaining method calls.
*/
public Builder setBlocking(boolean blocking) {
- // TODO
+ mConfig.blocking = blocking;
return this;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index aab7b1f..545b19a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -346,10 +346,6 @@
public abstract void noteWifiBatchedScanStoppedLocked(long elapsedRealtime);
public abstract void noteWifiMulticastEnabledLocked(long elapsedRealtime);
public abstract void noteWifiMulticastDisabledLocked(long elapsedRealtime);
- public abstract void noteAudioTurnedOnLocked(long elapsedRealtime);
- public abstract void noteAudioTurnedOffLocked(long elapsedRealtime);
- public abstract void noteVideoTurnedOnLocked(long elapsedRealtime);
- public abstract void noteVideoTurnedOffLocked(long elapsedRealtime);
public abstract void noteActivityResumedLocked(long elapsedRealtime);
public abstract void noteActivityPausedLocked(long elapsedRealtime);
public abstract long getWifiRunningTime(long elapsedRealtimeUs, int which);
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 207dc4a..c1e6664 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -19,7 +19,6 @@
import android.net.InterfaceConfiguration;
import android.net.INetworkManagementEventObserver;
-import android.net.LinkAddress;
import android.net.NetworkStats;
import android.net.RouteInfo;
import android.net.UidRange;
@@ -313,11 +312,6 @@
*/
void setDnsServersForNetwork(int netId, in String[] servers, String domains);
- /**
- * Flush the DNS cache associated with the specified network.
- */
- void flushNetworkDnsCache(int netId);
-
void setFirewallEnabled(boolean enabled);
boolean isFirewallEnabled();
void setFirewallInterfaceRule(String iface, boolean allow);
@@ -336,17 +330,6 @@
void removeVpnUidRanges(int netId, in UidRange[] ranges);
/**
- * Exempts {@code host} from the routing set up by {@link setMarkedForwardingRoute}
- * All connects to {@code host} will use the global routing table
- */
- void setHostExemption(in LinkAddress host);
-
- /**
- * Clears an exemption set by {@link setHostExemption}
- */
- void clearHostExemption(in LinkAddress host);
-
- /**
* Start the clatd (464xlat) service
*/
void startClatd(String interfaceName);
@@ -402,7 +385,6 @@
void removeInterfaceFromNetwork(String iface, int netId);
void addLegacyRouteForNetId(int netId, in RouteInfo routeInfo, int uid);
- void removeLegacyRouteForNetId(int netId, in RouteInfo routeInfo, int uid);
void setDefaultNetId(int netId);
void clearDefaultNetId();
@@ -419,4 +401,7 @@
* Deny UID from calling protect().
*/
void denyProtect(int uid);
+
+ void addInterfaceToLocalNetwork(String iface, in List<RouteInfo> routes);
+ void removeInterfaceFromLocalNetwork(String iface);
}
diff --git a/core/java/android/os/PersistableBundle.aidl b/core/java/android/os/PersistableBundle.aidl
new file mode 100644
index 0000000..5b05873
--- /dev/null
+++ b/core/java/android/os/PersistableBundle.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+parcelable PersistableBundle;
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 92e80a5..dda6d27 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -263,6 +263,12 @@
*/
public static final int BRIGHTNESS_OFF = 0;
+ /**
+ * Brightness value for default policy handling by the system.
+ * @hide
+ */
+ public static final int BRIGHTNESS_DEFAULT = -1;
+
// Note: Be sure to update android.os.BatteryStats and PowerManager.h
// if adding or modifying user activity event constants.
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 08a15eb..14f4a83 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -16,6 +16,8 @@
package android.os;
+import android.view.Display;
+
/**
* Power manager local system service interface.
*
@@ -53,6 +55,17 @@
*/
public abstract void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis);
+ /**
+ * Used by the dream manager to override certain properties while dozing.
+ *
+ * @param screenState The overridden screen state, or {@link Display.STATE_UNKNOWN}
+ * to disable the override.
+ * @param screenBrightness The overridden screen brightness, or
+ * {@link PowerManager#BRIGHTNESS_DEFAULT} to disable the override.
+ */
+ public abstract void setDozeOverrideFromDreamManager(
+ int screenState, int screenBrightness);
+
public abstract boolean getLowPowerModeEnabled();
public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index d3aee50..45edf28 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -310,6 +310,18 @@
*/
public static final String DISALLOW_CREATE_WINDOWS = "no_create_windows";
+ /**
+ * Key for user restrictions. Specifies if what is copied in the clipboard of this profile can
+ * be pasted in related profiles. Does not restrict if the clipboard of related profiles can be
+ * pasted in this profile.
+ * The default value is <code>false</code>.
+ * <p/>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CROSS_PROFILE_COPY_PASTE = "no_cross_profile_copy_paste";
+
/** @hide */
public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3;
/** @hide */
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 1a30c67..47cfa7d 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -17,12 +17,16 @@
package android.provider;
+import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.database.Cursor;
import android.net.Uri;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.ContactsContract.CommonDataKinds.Callable;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.DataUsageFeedback;
@@ -32,6 +36,8 @@
import com.android.internal.telephony.CallerInfo;
import com.android.internal.telephony.PhoneConstants;
+import java.util.List;
+
/**
* The CallLog provider contains information about placed and received calls.
*/
@@ -110,7 +116,8 @@
/**
* Content uri used to access call log entries, including voicemail records. You must have
- * the READ_CALL_LOG and WRITE_CALL_LOG permissions to read and write to the call log.
+ * the READ_CALL_LOG and WRITE_CALL_LOG permissions to read and write to the call log, as
+ * well as READ_VOICEMAIL and WRITE_VOICEMAIL permissions to read and write voicemails.
*/
public static final Uri CONTENT_URI_WITH_VOICEMAIL = CONTENT_URI.buildUpon()
.appendQueryParameter(ALLOW_VOICEMAILS_PARAM_KEY, "true")
@@ -258,6 +265,12 @@
public static final String VOICEMAIL_URI = "voicemail_uri";
/**
+ * Transcription of the call or voicemail entry. This will only be populated for call log
+ * entries of type {@link #VOICEMAIL_TYPE} that have valid transcriptions.
+ */
+ public static final String TRANSCRIPTION = "transcription";
+
+ /**
* Whether this item has been read or otherwise consumed by the user.
* <p>
* Unlike the {@link #NEW} field, which requires the user to have acknowledged the
@@ -348,12 +361,44 @@
* @param duration call duration in seconds
* @param dataUsage data usage for the call in bytes, null if data usage was not tracked for
* the call.
- *
+ * @result The URI of the call log entry belonging to the user that made or received this
+ * call.
* {@hide}
*/
public static Uri addCall(CallerInfo ci, Context context, String number,
int presentation, int callType, int features, PhoneAccountHandle accountHandle,
long start, int duration, Long dataUsage) {
+ return addCall(ci, context, number, presentation, callType, features, accountHandle,
+ start, duration, dataUsage, false);
+ }
+
+ /**
+ * Adds a call to the call log.
+ *
+ * @param ci the CallerInfo object to get the target contact from. Can be null
+ * if the contact is unknown.
+ * @param context the context used to get the ContentResolver
+ * @param number the phone number to be added to the calls db
+ * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
+ * is set by the network and denotes the number presenting rules for
+ * "allowed", "payphone", "restricted" or "unknown"
+ * @param callType enumerated values for "incoming", "outgoing", or "missed"
+ * @param features features of the call (e.g. Video).
+ * @param accountHandle The accountHandle object identifying the provider of the call
+ * @param start time stamp for the call in milliseconds
+ * @param duration call duration in seconds
+ * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for
+ * the call.
+ * @param addForAllUsers If true, the call is added to the call log of all currently
+ * running users. The caller must have the MANAGE_USERS permission if this is true.
+ *
+ * @result The URI of the call log entry belonging to the user that made or received this
+ * call.
+ * {@hide}
+ */
+ public static Uri addCall(CallerInfo ci, Context context, String number,
+ int presentation, int callType, int features, PhoneAccountHandle accountHandle,
+ long start, int duration, Long dataUsage, boolean addForAllUsers) {
final ContentResolver resolver = context.getContentResolver();
int numberPresentation = PRESENTATION_ALLOWED;
@@ -452,9 +497,32 @@
}
}
- Uri result = resolver.insert(CONTENT_URI, values);
+ Uri result = null;
- removeExpiredEntries(context);
+ if (addForAllUsers) {
+ // Insert the entry for all currently running users, in order to trigger any
+ // ContentObservers currently set on the call log.
+ final UserManager userManager = (UserManager) context.getSystemService(
+ Context.USER_SERVICE);
+ List<UserInfo> users = userManager.getUsers(true);
+ final int currentUserId = userManager.getUserHandle();
+ final int count = users.size();
+ for (int i = 0; i < count; i++) {
+ final UserInfo user = users.get(i);
+ final UserHandle userHandle = user.getUserHandle();
+ if (userManager.isUserRunning(userHandle) &&
+ !userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
+ userHandle)) {
+ Uri uri = addEntryAndRemoveExpiredEntries(context,
+ ContentProvider.maybeAddUserId(CONTENT_URI, user.id), values);
+ if (user.id == currentUserId) {
+ result = uri;
+ }
+ }
+ }
+ } else {
+ result = addEntryAndRemoveExpiredEntries(context, CONTENT_URI, values);
+ }
return result;
}
@@ -484,11 +552,14 @@
}
}
- private static void removeExpiredEntries(Context context) {
+ private static Uri addEntryAndRemoveExpiredEntries(Context context, Uri uri,
+ ContentValues values) {
final ContentResolver resolver = context.getContentResolver();
- resolver.delete(CONTENT_URI, "_id IN " +
+ Uri result = resolver.insert(uri, values);
+ resolver.delete(uri, "_id IN " +
"(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
+ " LIMIT -1 OFFSET 500)", null);
+ return result;
}
}
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 829d459..d0dbd96 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8536,6 +8536,12 @@
"com.android.contacts.action.LIST_FREQUENT";
/**
+ * The action for the "Join Contact" picker.
+ */
+ public static final String PICK_JOIN_CONTACT_ACTION =
+ "com.android.contacts.action.JOIN_CONTACT";
+
+ /**
* The action for the "strequent" contacts list tab. It first lists the starred
* contacts in alphabetical order and then the frequent contacts in descending
* order of the number of times they have been contacted.
@@ -8567,6 +8573,15 @@
*/
public static final String FILTER_TEXT_EXTRA_KEY =
"com.android.contacts.extra.FILTER_TEXT";
+
+ /**
+ * Used with JOIN_CONTACT action to set the target for aggregation. This action type
+ * uses contact ids instead of contact uris for the sake of backwards compatibility.
+ * <p>
+ * Type: LONG
+ */
+ public static final String TARGET_CONTACT_ID_EXTRA_KEY
+ = "com.android.contacts.action.CONTACT_ID";
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bbaf1c1..34d7c80 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3937,30 +3937,12 @@
"accessibility_captioning_font_scale";
/**
- * Setting that specifies whether the quick setting tile for display
- * color inversion is enabled.
- *
- * @hide
- */
- public static final String ACCESSIBILITY_DISPLAY_INVERSION_QUICK_SETTING_ENABLED =
- "accessibility_display_inversion_quick_setting_enabled";
-
- /**
* Setting that specifies whether display color inversion is enabled.
*/
public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED =
"accessibility_display_inversion_enabled";
/**
- * Setting that specifies whether the quick setting tile for display
- * color space adjustment is enabled.
- *
- * @hide
- */
- public static final String ACCESSIBILITY_DISPLAY_DALTONIZER_QUICK_SETTING_ENABLED =
- "accessibility_display_daltonizer_quick_setting_enabled";
-
- /**
* Setting that specifies whether display color space adjustment is
* enabled.
*
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 5fa542a..5cf8aa6 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -27,10 +27,13 @@
import android.graphics.drawable.ColorDrawable;
import android.os.Handler;
import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
import android.view.ActionMode;
+import android.view.Display;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
@@ -42,6 +45,7 @@
import android.view.WindowManagerGlobal;
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
+import android.util.MathUtils;
import com.android.internal.policy.PolicyManager;
import com.android.internal.util.DumpUtils;
@@ -133,8 +137,11 @@
* android:exported="true"
* android:icon="@drawable/my_icon"
* android:label="@string/my_dream_label"
- * android:permission="android.permission.BIND_DREAM_SERVICE" >
- * ...
+ * android:permission="android.permission.BIND_DREAM_SERVICE">
+ * <intent-filter>
+ * <action android:name=”android.service.dreams.DreamService” />
+ * <category android:name=”android.intent.category.DEFAULT” />
+ * </intent-filter>
* </service>
* </pre>
*/
@@ -177,6 +184,8 @@
private boolean mDozing;
private boolean mWindowless;
private DozeHardware mDozeHardware;
+ private int mDozeScreenState = Display.STATE_UNKNOWN;
+ private int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
private boolean mDebug = false;
@@ -560,7 +569,7 @@
*
* @return True if this dream can doze.
* @see #startDozing
- * @hide experimental
+ * @hide For use by system UI components only.
*/
public boolean canDoze() {
return mCanDoze;
@@ -593,13 +602,19 @@
* </p>
*
* @see #stopDozing
- * @hide experimental
+ * @hide For use by system UI components only.
*/
public void startDozing() {
if (mCanDoze && !mDozing) {
mDozing = true;
+ updateDoze();
+ }
+ }
+
+ private void updateDoze() {
+ if (mDozing) {
try {
- mSandman.startDozing(mWindowToken);
+ mSandman.startDozing(mWindowToken, mDozeScreenState, mDozeScreenBrightness);
} catch (RemoteException ex) {
// system server died
}
@@ -615,7 +630,7 @@
* </p>
*
* @see #startDozing
- * @hide experimental
+ * @hide For use by system UI components only.
*/
public void stopDozing() {
if (mDozing) {
@@ -636,7 +651,7 @@
* @return True if the dream is dozing.
*
* @see #setDozing(boolean)
- * @hide experimental
+ * @hide For use by system UI components only.
*/
public boolean isDozing() {
return mDozing;
@@ -649,7 +664,7 @@
* @return An instance of {@link DozeHardware} or null if this device does not offer
* hardware support for dozing.
*
- * @hide experimental
+ * @hide For use by system UI components only.
*/
public DozeHardware getDozeHardware() {
if (mCanDoze && mDozeHardware == null && mWindowToken != null) {
@@ -666,11 +681,116 @@
}
/**
+ * Gets the screen state to use while dozing.
+ *
+ * @return The screen state to use while dozing, such as {@link Display#STATE_ON},
+ * {@link Display#STATE_DOZE}, {@link Display#STATE_DOZE_SUSPEND},
+ * or {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN} for the default
+ * behavior.
+ *
+ * @see #setDozeScreenState
+ * @hide For use by system UI components only.
+ */
+ public int getDozeScreenState() {
+ return mDozeScreenState;
+ }
+
+ /**
+ * Sets the screen state to use while dozing.
+ * <p>
+ * The value of this property determines the power state of the primary display
+ * once {@link #startDozing} has been called. The default value is
+ * {@link Display#STATE_UNKNOWN} which lets the system decide.
+ * The dream may set a different state before starting to doze and may
+ * perform transitions between states while dozing to conserve power and
+ * achieve various effects.
+ * </p><p>
+ * It is recommended that the state be set to {@link Display#STATE_DOZE_SUSPEND}
+ * once the dream has completely finished drawing and before it releases its wakelock
+ * to allow the display hardware to be fully suspended. While suspended, the
+ * display will preserve its on-screen contents or hand off control to dedicated
+ * doze hardware if the devices supports it. If the doze suspend state is
+ * used, the dream must make sure to set the mode back
+ * to {@link Display#STATE_DOZE} or {@link Display#STATE_ON} before drawing again
+ * since the display updates may be ignored and not seen by the user otherwise.
+ * </p><p>
+ * The set of available display power states and their behavior while dozing is
+ * hardware dependent and may vary across devices. The dream may therefore
+ * need to be modified or configured to correctly support the hardware.
+ * </p>
+ *
+ * @param state The screen state to use while dozing, such as {@link Display#STATE_ON},
+ * {@link Display#STATE_DOZE}, {@link Display#STATE_DOZE_SUSPEND},
+ * or {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN} for the default
+ * behavior.
+ *
+ * @hide For use by system UI components only.
+ */
+ public void setDozeScreenState(int state) {
+ if (mDozeScreenState != state) {
+ mDozeScreenState = state;
+ updateDoze();
+ }
+ }
+
+ /**
+ * Gets the screen brightness to use while dozing.
+ *
+ * @return The screen brightness while dozing as a value between
+ * {@link PowerManager#BRIGHTNESS_OFF} (0) and {@link PowerManager#BRIGHTNESS_ON} (255),
+ * or {@link PowerManager#BRIGHTNESS_DEFAULT} (-1) to ask the system to apply
+ * its default policy based on the screen state.
+ *
+ * @see #setDozeScreenBrightness
+ * @hide For use by system UI components only.
+ */
+ public int getDozeScreenBrightness() {
+ return mDozeScreenBrightness;
+ }
+
+ /**
+ * Sets the screen brightness to use while dozing.
+ * <p>
+ * The value of this property determines the power state of the primary display
+ * once {@link #startDozing} has been called. The default value is
+ * {@link PowerManager#BRIGHTNESS_DEFAULT} which lets the system decide.
+ * The dream may set a different brightness before starting to doze and may adjust
+ * the brightness while dozing to conserve power and achieve various effects.
+ * </p><p>
+ * Note that dream may specify any brightness in the full 0-255 range, including
+ * values that are less than the minimum value for manual screen brightness
+ * adjustments by the user. In particular, the value may be set to 0 which may
+ * turn off the backlight entirely while still leaving the screen on although
+ * this behavior is device dependent and not guaranteed.
+ * </p><p>
+ * The available range of display brightness values and their behavior while dozing is
+ * hardware dependent and may vary across devices. The dream may therefore
+ * need to be modified or configured to correctly support the hardware.
+ * </p>
+ *
+ * @param brightness The screen brightness while dozing as a value between
+ * {@link PowerManager#BRIGHTNESS_OFF} (0) and {@link PowerManager#BRIGHTNESS_ON} (255),
+ * or {@link PowerManager#BRIGHTNESS_DEFAULT} (-1) to ask the system to apply
+ * its default policy based on the screen state.
+ *
+ * @hide For use by system UI components only.
+ */
+ public void setDozeScreenBrightness(int brightness) {
+ if (brightness != PowerManager.BRIGHTNESS_DEFAULT) {
+ brightness = clampAbsoluteBrightness(brightness);
+ }
+ if (mDozeScreenBrightness != brightness) {
+ mDozeScreenBrightness = brightness;
+ updateDoze();
+ }
+ }
+
+ /**
* Called when this Dream is constructed.
*/
@Override
public void onCreate() {
- if (mDebug) Slog.v(TAG, "onCreate() on thread " + Thread.currentThread().getId());
+ if (mDebug) Slog.v(TAG, "onCreate()");
super.onCreate();
}
@@ -844,8 +964,6 @@
return;
}
- if (mDebug) Slog.v(TAG, "Attached on thread " + Thread.currentThread().getId());
-
mWindowToken = windowToken;
mCanDoze = canDoze;
if (mWindowless && !mCanDoze) {
@@ -963,7 +1081,17 @@
if (isScreenBright()) pw.print(" bright");
if (isWindowless()) pw.print(" windowless");
if (isDozing()) pw.print(" dozing");
+ else if (canDoze()) pw.print(" candoze");
pw.println();
+ if (canDoze()) {
+ pw.println(" doze hardware: " + mDozeHardware);
+ pw.println(" doze screen state: " + Display.stateToString(mDozeScreenState));
+ pw.println(" doze screen brightness: " + mDozeScreenBrightness);
+ }
+ }
+
+ private static int clampAbsoluteBrightness(int value) {
+ return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
}
private final class DreamServiceWrapper extends IDreamService.Stub {
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 9608a4d..648426c 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -32,7 +32,7 @@
void testDream(in ComponentName componentName);
boolean isDreaming();
void finishSelf(in IBinder token, boolean immediate);
- void startDozing(in IBinder token);
+ void startDozing(in IBinder token, int screenState, int screenBrightness);
void stopDozing(in IBinder token);
IDozeHardware getDozeHardware(in IBinder token);
-}
\ No newline at end of file
+}
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index b3705d8..19a8ef5 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -28,4 +28,5 @@
void onNotificationRemoved(in StatusBarNotification notification,
in NotificationRankingUpdate update);
void onNotificationRankingUpdate(in NotificationRankingUpdate update);
+ void onListenerFlagsChanged(int flags);
}
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 7d5ff33..1fc64e4 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -53,6 +53,12 @@
private final String TAG = NotificationListenerService.class.getSimpleName()
+ "[" + getClass().getSimpleName() + "]";
+ /** {@link #getCurrentListenerFlags() Listener flags} constant - default state. **/
+ public static final int FLAG_NONE = 0;
+ /** {@link #getCurrentListenerFlags() Listener flags} constant - the primary device UI
+ * should disable notification sound, vibrating and other visual or aural effects. **/
+ public static final int FLAG_DISABLE_HOST_ALERTS = 1;
+
private INotificationListenerWrapper mWrapper = null;
private RankingMap mRankingMap;
@@ -156,6 +162,16 @@
// optional
}
+ /**
+ * Implement this method to be notified when the
+ * {@link #getCurrentListenerFlags() listener flags} change.
+ *
+ * @param flags The current {@link #getCurrentListenerFlags() listener flags}.
+ */
+ public void onListenerFlagsChanged(int flags) {
+ // optional
+ }
+
private final INotificationManager getNotificationInterface() {
if (mNoMan == null) {
mNoMan = INotificationManager.Stub.asInterface(
@@ -279,6 +295,46 @@
}
/**
+ * Gets the set of flags representing current state.
+ *
+ * <p>
+ * The current state may differ from the requested state if the flag represents state
+ * shared across all listeners or a feature the notification host does not support or refuses
+ * to grant.
+ *
+ * @return One or more of the FLAG_ constants.
+ */
+ public final int getCurrentListenerFlags() {
+ if (!isBound()) return FLAG_NONE;
+ try {
+ return getNotificationInterface().getFlagsFromListener(mWrapper);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ return FLAG_NONE;
+ }
+ }
+
+ /**
+ * Sets the desired {@link #getCurrentListenerFlags() listener flags}.
+ *
+ * <p>
+ * This is merely a request, the host may or not choose to take action depending
+ * on other listener requests or other global state.
+ * <p>
+ * Listen for updates using {@link #onListenerFlagsChanged(int)}.
+ *
+ * @param flags One or more of the FLAG_ constants.
+ */
+ public final void requestListenerFlags(int flags) {
+ if (!isBound()) return;
+ try {
+ getNotificationInterface().requestFlagsFromListener(mWrapper, flags);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ /**
* Returns current ranking information.
*
* <p>
@@ -402,6 +458,14 @@
}
}
}
+ @Override
+ public void onListenerFlagsChanged(int flags) throws RemoteException {
+ try {
+ NotificationListenerService.this.onListenerFlagsChanged(flags);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onListenerFlagsChanged", t);
+ }
+ }
}
private void applyUpdate(NotificationRankingUpdate update) {
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index e7cdc4e..e5a5292 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -30,6 +30,7 @@
private final int id;
private final String tag;
private final String key;
+ private final String groupKey;
private final int uid;
private final String opPkg;
@@ -65,6 +66,7 @@
this.notification.setUser(user);
this.postTime = postTime;
this.key = key();
+ this.groupKey = groupKey();
}
public StatusBarNotification(Parcel in) {
@@ -84,12 +86,26 @@
this.notification.setUser(this.user);
this.postTime = in.readLong();
this.key = key();
+ this.groupKey = groupKey();
}
private String key() {
return user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid;
}
+ private String groupKey() {
+ final String group = getNotification().getGroup();
+ final String sortKey = getNotification().getSortKey();
+ if (group == null && sortKey == null) {
+ // a group of one
+ return key;
+ }
+ return user.getIdentifier() + "|" + pkg + "|" +
+ (group == null
+ ? "p:" + notification.priority
+ : "g:" + group);
+ }
+
public void writeToParcel(Parcel out, int flags) {
out.writeString(this.pkg);
out.writeString(this.opPkg);
@@ -240,4 +256,11 @@
public String getKey() {
return key;
}
+
+ /**
+ * A key that indicates the group with which this message ranks.
+ */
+ public String getGroupKey() {
+ return groupKey;
+ }
}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index ad30f44..d685cc5 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -27,6 +27,7 @@
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
@@ -41,26 +42,37 @@
* always-on keyphrase detection APIs.
*/
public class AlwaysOnHotwordDetector {
- //---- States of Keyphrase availability ----//
+ //---- States of Keyphrase availability. Return codes for onAvailabilityChanged() ----//
/**
- * Indicates that the given keyphrase is not available on the system because of the
- * hardware configuration.
+ * Indicates that this hotword detector is no longer valid for any recognition
+ * and should not be used anymore.
*/
- public static final int KEYPHRASE_HARDWARE_UNAVAILABLE = -2;
+ public static final int STATE_INVALID = -3;
/**
- * Indicates that the given keyphrase is not supported.
+ * Indicates that recognition for the given keyphrase is not available on the system
+ * because of the hardware configuration.
*/
- public static final int KEYPHRASE_UNSUPPORTED = -1;
+ public static final int STATE_HARDWARE_UNAVAILABLE = -2;
+ /**
+ * Indicates that recognition for the given keyphrase is not supported.
+ */
+ public static final int STATE_KEYPHRASE_UNSUPPORTED = -1;
/**
* Indicates that the given keyphrase is not enrolled.
*/
- public static final int KEYPHRASE_UNENROLLED = 1;
+ public static final int STATE_KEYPHRASE_UNENROLLED = 1;
/**
- * Indicates that the given keyphrase is currently enrolled but not being actively listened for.
+ * Indicates that the given keyphrase is currently enrolled and it's possible to start
+ * recognition for it.
*/
- public static final int KEYPHRASE_ENROLLED = 2;
+ public static final int STATE_KEYPHRASE_ENROLLED = 2;
- // Keyphrase management actions ----//
+ /**
+ * Indicates that the detector isn't ready currently.
+ */
+ private static final int STATE_NOT_READY = 0;
+
+ // Keyphrase management actions. Used in getManageIntent() ----//
/** Indicates that we need to enroll. */
public static final int MANAGE_ACTION_ENROLL = 0;
/** Indicates that we need to re-enroll. */
@@ -83,7 +95,7 @@
*/
public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 0x1;
- //---- Recognition mode flags ----//
+ //---- Recognition mode flags. Return codes for getSupportedRecognitionModes() ----//
// Must be kept in sync with the related attribute defined as searchKeyphraseRecognitionFlags.
/**
@@ -98,9 +110,12 @@
= SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
static final String TAG = "AlwaysOnHotwordDetector";
+ // TODO: Set to false.
+ static final boolean DBG = true;
- private static final int MSG_HOTWORD_DETECTED = 1;
- private static final int MSG_DETECTION_STOPPED = 2;
+ private static final int MSG_STATE_CHANGED = 1;
+ private static final int MSG_HOTWORD_DETECTED = 2;
+ private static final int MSG_DETECTION_STOPPED = 3;
private final String mText;
private final String mLocale;
@@ -109,23 +124,45 @@
* This may be null if this keyphrase isn't supported by the enrollment application.
*/
private final KeyphraseMetadata mKeyphraseMetadata;
- /**
- * The sound model for the keyphrase, derived from the model management service
- * (IVoiceInteractionManagerService). May be null if the keyphrase isn't enrolled yet.
- */
- private final KeyphraseSoundModel mEnrolledSoundModel;
private final KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
private final IVoiceInteractionService mVoiceInteractionService;
private final IVoiceInteractionManagerService mModelManagementService;
private final SoundTriggerListener mInternalCallback;
private final Callback mExternalCallback;
- private final boolean mDisabled;
+ private final Object mLock = new Object();
+ private final Handler mHandler;
+
+ /**
+ * The sound model for the keyphrase, derived from the model management service
+ * (IVoiceInteractionManagerService). May be null if the keyphrase isn't enrolled yet.
+ */
+ private KeyphraseSoundModel mEnrolledSoundModel;
+ private int mAvailability = STATE_NOT_READY;
/**
* Callbacks for always-on hotword detection.
*/
public interface Callback {
/**
+ * Called when the hotword availability changes.
+ * This indicates a change in the availability of recognition for the given keyphrase.
+ * It's called at least once with the initial availability.<p/>
+ *
+ * Availability implies whether the hardware on this system is capable of listening for
+ * the given keyphrase or not. <p/>
+ * If the return code is one of {@link #STATE_HARDWARE_UNAVAILABLE} or
+ * {@link #STATE_KEYPHRASE_UNSUPPORTED},
+ * detection is not possible and no further interaction should be
+ * performed with this detector. <br/>
+ * If it is {@link #STATE_KEYPHRASE_UNENROLLED} the caller may choose to begin
+ * an enrollment flow for the keyphrase. <br/>
+ * and for {@link #STATE_KEYPHRASE_ENROLLED} a recognition can be started as desired. <p/>
+ *
+ * If the return code is {@link #STATE_INVALID}, this detector is stale.
+ * A new detector should be obtained for use in the future.
+ */
+ void onAvailabilityChanged(int status);
+ /**
* Called when the keyphrase is spoken.
*
* @param data Optional trigger audio data, if it was requested during
@@ -156,51 +193,30 @@
mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo;
mKeyphraseMetadata = mKeyphraseEnrollmentInfo.getKeyphraseMetadata(text, locale);
mExternalCallback = callback;
- mInternalCallback = new SoundTriggerListener(new MyHandler());
+ mHandler = new MyHandler();
+ mInternalCallback = new SoundTriggerListener(mHandler);
mVoiceInteractionService = voiceInteractionService;
mModelManagementService = modelManagementService;
- if (mKeyphraseMetadata != null) {
- mEnrolledSoundModel = internalGetKeyphraseSoundModel(mKeyphraseMetadata.id);
- } else {
- mEnrolledSoundModel = null;
- }
- int initialAvailability = internalGetAvailabilityLocked();
- mDisabled = (initialAvailability == KEYPHRASE_HARDWARE_UNAVAILABLE)
- || (initialAvailability == KEYPHRASE_UNSUPPORTED);
- }
-
- /**
- * Gets the state of always-on hotword detection for the given keyphrase and locale
- * on this system.
- * Availability implies that the hardware on this system is capable of listening for
- * the given keyphrase or not.
- *
- * @return Indicates if always-on hotword detection is available for the given keyphrase.
- * The return code is one of {@link #KEYPHRASE_HARDWARE_UNAVAILABLE},
- * {@link #KEYPHRASE_UNSUPPORTED}, {@link #KEYPHRASE_UNENROLLED} or
- * {@link #KEYPHRASE_ENROLLED}.
- */
- public int getAvailability() {
- synchronized (this) {
- return internalGetAvailabilityLocked();
- }
+ new RefreshAvailabiltyTask().execute();
}
/**
* Gets the recognition modes supported by the associated keyphrase.
*
* @throws UnsupportedOperationException if the keyphrase itself isn't supported.
- * Callers should check the availability by calling {@link #getAvailability()}
- * before calling this method to avoid this exception.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
*/
public int getSupportedRecognitionModes() {
- synchronized (this) {
+ synchronized (mLock) {
return getSupportedRecognitionModesLocked();
}
}
private int getSupportedRecognitionModesLocked() {
- if (mDisabled) {
+ // This method only makes sense if we can actually support a recognition.
+ if (mAvailability != STATE_KEYPHRASE_ENROLLED
+ && mAvailability != STATE_KEYPHRASE_UNENROLLED) {
throw new UnsupportedOperationException(
"Getting supported recognition modes for the keyphrase is not supported");
}
@@ -216,17 +232,18 @@
* {@link #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO}.
* @return {@link #STATUS_OK} if the call succeeds, an error code otherwise.
* @throws UnsupportedOperationException if the recognition isn't supported.
- * Callers should check the availability by calling {@link #getAvailability()}
- * before calling this method to avoid this exception.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
*/
public int startRecognition(int recognitionFlags) {
- synchronized (this) {
+ synchronized (mLock) {
return startRecognitionLocked(recognitionFlags);
}
}
private int startRecognitionLocked(int recognitionFlags) {
- if (internalGetAvailabilityLocked() != KEYPHRASE_ENROLLED) {
+ // This method only makes sense if we can start a recognition.
+ if (mAvailability != STATE_KEYPHRASE_ENROLLED) {
throw new UnsupportedOperationException(
"Recognition for the given keyphrase is not supported");
}
@@ -257,17 +274,18 @@
*
* @return {@link #STATUS_OK} if the call succeeds, an error code otherwise.
* @throws UnsupportedOperationException if the recognition isn't supported.
- * Callers should check the availability by calling {@link #getAvailability()}
- * before calling this method to avoid this exception.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
*/
public int stopRecognition() {
- synchronized (this) {
+ synchronized (mLock) {
return stopRecognitionLocked();
}
}
- private synchronized int stopRecognitionLocked() {
- if (internalGetAvailabilityLocked() != KEYPHRASE_ENROLLED) {
+ private int stopRecognitionLocked() {
+ // This method only makes sense if we can start a recognition.
+ if (mAvailability != STATE_KEYPHRASE_ENROLLED) {
throw new UnsupportedOperationException(
"Recognition for the given keyphrase is not supported");
}
@@ -294,11 +312,13 @@
* {@link #MANAGE_ACTION_UN_ENROLL}.
* @return An {@link Intent} to manage the given keyphrase.
* @throws UnsupportedOperationException if managing they keyphrase isn't supported.
- * Callers should check the availability by calling {@link #getAvailability()}
- * before calling this method to avoid this exception.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
*/
public Intent getManageIntent(int action) {
- if (mDisabled) {
+ // This method only makes sense if we can actually support a recognition.
+ if (mAvailability != STATE_KEYPHRASE_ENROLLED
+ && mAvailability != STATE_KEYPHRASE_UNENROLLED) {
throw new UnsupportedOperationException(
"Managing the given keyphrase is not supported");
}
@@ -311,55 +331,44 @@
return mKeyphraseEnrollmentInfo.getManageKeyphraseIntent(action, mText, mLocale);
}
- private int internalGetAvailabilityLocked() {
- ModuleProperties dspModuleProperties = null;
- try {
- dspModuleProperties =
- mModelManagementService.getDspModuleProperties(mVoiceInteractionService);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in getDspProperties!");
+ /**
+ * Invalidates this hotword detector so that any future calls to this result
+ * in an IllegalStateException.
+ *
+ * @hide
+ */
+ void invalidate() {
+ synchronized (mLock) {
+ mAvailability = STATE_INVALID;
+ notifyStateChangedLocked();
}
- // No DSP available
- if (dspModuleProperties == null) {
- return KEYPHRASE_HARDWARE_UNAVAILABLE;
- }
- // No enrollment application supports this keyphrase/locale
- if (mKeyphraseMetadata == null) {
- return KEYPHRASE_UNSUPPORTED;
- }
- // This keyphrase hasn't been enrolled.
- if (mEnrolledSoundModel == null) {
- return KEYPHRASE_UNENROLLED;
- }
- return KEYPHRASE_ENROLLED;
}
/**
- * @return The corresponding {@link KeyphraseSoundModel} or null if none is found.
+ * Reloads the sound models from the service.
+ *
+ * @hide
*/
- private KeyphraseSoundModel internalGetKeyphraseSoundModel(int keyphraseId) {
- List<KeyphraseSoundModel> soundModels;
- try {
- soundModels = mModelManagementService
- .listRegisteredKeyphraseSoundModels(mVoiceInteractionService);
- if (soundModels == null || soundModels.isEmpty()) {
- Slog.i(TAG, "No available sound models for keyphrase ID: " + keyphraseId);
- return null;
+ void onSoundModelsChanged() {
+ synchronized (mLock) {
+ // TODO: This should stop the recognition if it was using an enrolled sound model
+ // that's no longer available.
+ if (mAvailability == STATE_INVALID
+ || mAvailability == STATE_HARDWARE_UNAVAILABLE
+ || mAvailability == STATE_KEYPHRASE_UNSUPPORTED) {
+ Slog.w(TAG, "Received onSoundModelsChanged for an unsupported keyphrase/config");
+ return;
}
- for (KeyphraseSoundModel soundModel : soundModels) {
- if (soundModel.keyphrases == null) {
- continue;
- }
- for (Keyphrase keyphrase : soundModel.keyphrases) {
- if (keyphrase.id == keyphraseId) {
- return soundModel;
- }
- }
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in listRegisteredKeyphraseSoundModels!");
+
+ // Execute a refresh availability task - which should then notify of a change.
+ new RefreshAvailabiltyTask().execute();
}
- return null;
+ }
+
+ private void notifyStateChangedLocked() {
+ Message message = Message.obtain(mHandler, MSG_STATE_CHANGED);
+ message.arg1 = mAvailability;
+ message.sendToTarget();
}
/** @hide */
@@ -389,6 +398,9 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_STATE_CHANGED:
+ mExternalCallback.onAvailabilityChanged(msg.arg1);
+ break;
case MSG_HOTWORD_DETECTED:
mExternalCallback.onDetected((byte[]) msg.obj);
break;
@@ -399,4 +411,95 @@
}
}
}
+
+ class RefreshAvailabiltyTask extends AsyncTask<Void, Void, Void> {
+
+ @Override
+ public Void doInBackground(Void... params) {
+ int availability = internalGetInitialAvailability();
+ KeyphraseSoundModel soundModel = null;
+ // Fetch the sound model if the availability is one of the supported ones.
+ if (availability == STATE_NOT_READY
+ || availability == STATE_KEYPHRASE_UNENROLLED
+ || availability == STATE_KEYPHRASE_ENROLLED) {
+ soundModel =
+ internalGetKeyphraseSoundModel(mKeyphraseMetadata.id);
+ if (soundModel == null) {
+ availability = STATE_KEYPHRASE_UNENROLLED;
+ } else {
+ availability = STATE_KEYPHRASE_ENROLLED;
+ }
+ }
+
+ synchronized (mLock) {
+ if (DBG) {
+ Slog.d(TAG, "Hotword availability changed from " + mAvailability
+ + " -> " + availability);
+ }
+ mAvailability = availability;
+ mEnrolledSoundModel = soundModel;
+ notifyStateChangedLocked();
+ }
+ return null;
+ }
+
+ /**
+ * @return The initial availability without checking the enrollment status.
+ */
+ private int internalGetInitialAvailability() {
+ synchronized (mLock) {
+ // This detector has already been invalidated.
+ if (mAvailability == STATE_INVALID) {
+ return STATE_INVALID;
+ }
+ }
+
+ ModuleProperties dspModuleProperties = null;
+ try {
+ dspModuleProperties =
+ mModelManagementService.getDspModuleProperties(mVoiceInteractionService);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in getDspProperties!");
+ }
+ // No DSP available
+ if (dspModuleProperties == null) {
+ return STATE_HARDWARE_UNAVAILABLE;
+ }
+ // No enrollment application supports this keyphrase/locale
+ if (mKeyphraseMetadata == null) {
+ return STATE_KEYPHRASE_UNSUPPORTED;
+ }
+ return STATE_NOT_READY;
+ }
+
+ /**
+ * @return The corresponding {@link KeyphraseSoundModel} or null if none is found.
+ */
+ private KeyphraseSoundModel internalGetKeyphraseSoundModel(int keyphraseId) {
+ List<KeyphraseSoundModel> soundModels;
+ try {
+ soundModels = mModelManagementService
+ .listRegisteredKeyphraseSoundModels(mVoiceInteractionService);
+ if (soundModels == null || soundModels.isEmpty()) {
+ Slog.i(TAG, "No available sound models for keyphrase ID: " + keyphraseId);
+ return null;
+ }
+ for (int i = 0; i < soundModels.size(); i++) {
+ KeyphraseSoundModel soundModel = soundModels.get(i);
+ if (soundModel.keyphrases == null || soundModel.keyphrases.length == 0) {
+ continue;
+ }
+ for (int j = 0; i < soundModel.keyphrases.length; j++) {
+ Keyphrase keyphrase = soundModel.keyphrases[j];
+ if (keyphrase.id == keyphraseId) {
+ return soundModel;
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in listRegisteredKeyphraseSoundModels!");
+ }
+ return null;
+ }
+ }
}
diff --git a/core/java/android/service/voice/IVoiceInteractionService.aidl b/core/java/android/service/voice/IVoiceInteractionService.aidl
index c9915a2..e8265a2 100644
--- a/core/java/android/service/voice/IVoiceInteractionService.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionService.aidl
@@ -21,4 +21,6 @@
*/
oneway interface IVoiceInteractionService {
void ready();
+ void soundModelsChanged();
+ void shutdown();
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 982f43d..82e23c4 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -28,8 +28,8 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
-
import android.provider.Settings;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractionManagerService;
@@ -70,15 +70,27 @@
@Override public void ready() {
mHandler.sendEmptyMessage(MSG_READY);
}
+ @Override public void shutdown() {
+ mHandler.sendEmptyMessage(MSG_SHUTDOWN);
+ }
+ @Override public void soundModelsChanged() {
+ mHandler.sendEmptyMessage(MSG_SOUND_MODELS_CHANGED);
+ }
};
MyHandler mHandler;
IVoiceInteractionManagerService mSystemService;
+ private final Object mLock = new Object();
+
private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
+ private AlwaysOnHotwordDetector mHotwordDetector;
+
static final int MSG_READY = 1;
+ static final int MSG_SHUTDOWN = 2;
+ static final int MSG_SOUND_MODELS_CHANGED = 3;
class MyHandler extends Handler {
@Override
@@ -87,6 +99,12 @@
case MSG_READY:
onReady();
break;
+ case MSG_SHUTDOWN:
+ onShutdownInternal();
+ break;
+ case MSG_SOUND_MODELS_CHANGED:
+ onSoundModelsChangedInternal();
+ break;
default:
super.handleMessage(msg);
}
@@ -140,9 +158,10 @@
/**
* Called during service initialization to tell you when the system is ready
- * to receive interaction from it. You should generally do initialization here
- * rather than in {@link #onCreate()}. Methods such as {@link #startSession}
- * and {@link #getAlwaysOnHotwordDetector} will not be operational until this point.
+ * to receive interaction from it. You should generally do initialization here
+ * rather than in {@link #onCreate()}. Methods such as {@link #startSession(Bundle)} and
+ * {@link #createAlwaysOnHotwordDetector(String, String, android.service.voice.AlwaysOnHotwordDetector.Callback)}
+ * will not be operational until this point.
*/
public void onReady() {
mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
@@ -150,22 +169,56 @@
mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager());
}
+ private void onShutdownInternal() {
+ onShutdown();
+ // Stop any active recognitions when shutting down.
+ // This ensures that if implementations forget to stop any active recognition,
+ // It's still guaranteed to have been stopped.
+ // This helps with cases where the voice interaction implementation is changed
+ // by the user.
+ safelyShutdownHotwordDetector();
+ }
+
/**
+ * Called during service de-initialization to tell you when the system is shutting the
+ * service down.
+ */
+ public void onShutdown() {
+ }
+
+ private void onSoundModelsChangedInternal() {
+ synchronized (this) {
+ if (mHotwordDetector != null) {
+ // TODO: Stop recognition if a sound model that was being recognized gets deleted.
+ mHotwordDetector.onSoundModelsChanged();
+ }
+ }
+ }
+
+ /**
+ * Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
+ * This instance must be retained and used by the client.
+ * Calling this a second time invalidates the previously created hotword detector
+ * which can no longer be used to manage recognition.
+ *
* @param keyphrase The keyphrase that's being used, for example "Hello Android".
* @param locale The locale for which the enrollment needs to be performed.
* This is a Java locale, for example "en_US".
* @param callback The callback to notify of detection events.
* @return An always-on hotword detector for the given keyphrase and locale.
*/
- public final AlwaysOnHotwordDetector getAlwaysOnHotwordDetector(
+ public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(
String keyphrase, String locale, AlwaysOnHotwordDetector.Callback callback) {
if (mSystemService == null) {
throw new IllegalStateException("Not available until onReady() is called");
}
- // TODO: Cache instances and return the same one instead of creating a new interactor
- // for the same keyphrase/locale combination.
- return new AlwaysOnHotwordDetector(keyphrase, locale, callback,
- mKeyphraseEnrollmentInfo, mInterface, mSystemService);
+ synchronized (mLock) {
+ // Allow only one concurrent recognition via the APIs.
+ safelyShutdownHotwordDetector();
+ mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback,
+ mKeyphraseEnrollmentInfo, mInterface, mSystemService);
+ }
+ return mHotwordDetector;
}
/**
@@ -176,4 +229,18 @@
protected final KeyphraseEnrollmentInfo getKeyphraseEnrollmentInfo() {
return mKeyphraseEnrollmentInfo;
}
+
+ private void safelyShutdownHotwordDetector() {
+ try {
+ synchronized (mLock) {
+ if (mHotwordDetector != null) {
+ mHotwordDetector.stopRecognition();
+ mHotwordDetector.invalidate();
+ mHotwordDetector = null;
+ }
+ }
+ } catch (Exception ex) {
+ // Ignore.
+ }
+ }
}
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index f34e746..aa6ad20 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -16,19 +16,38 @@
package android.text.format;
-import android.content.res.Resources;
+import android.util.TimeFormatException;
+import java.io.IOException;
import java.util.Locale;
import java.util.TimeZone;
-import libcore.icu.LocaleData;
+import libcore.util.ZoneInfo;
+import libcore.util.ZoneInfoDB;
/**
* An alternative to the {@link java.util.Calendar} and
* {@link java.util.GregorianCalendar} classes. An instance of the Time class represents
* a moment in time, specified with second precision. It is modelled after
- * struct tm, and in fact, uses struct tm to implement most of the
- * functionality.
+ * struct tm. This class is not thread-safe and does not consider leap seconds.
+ *
+ * <p>This class has a number of issues and it is recommended that
+ * {@link java.util.GregorianCalendar} is used instead.
+ *
+ * <p>Known issues:
+ * <ul>
+ * <li>For historical reasons when performing time calculations all arithmetic currently takes
+ * place using 32-bit integers. This limits the reliable time range representable from 1902
+ * until 2037.See the wikipedia article on the
+ * <a href="http://en.wikipedia.org/wiki/Year_2038_problem">Year 2038 problem</a> for details.
+ * Do not rely on this behavior; it may change in the future.
+ * </li>
+ * <li>Calling {@link #switchTimezone(String)} on a date that cannot exist, such as a wall time
+ * that was skipped due to a DST transition, will result in a date in 1969 (i.e. -1, or 1 second
+ * before 1st Jan 1970 UTC).</li>
+ * <li>Much of the formatting / parsing assumes ASCII text and is therefore not suitable for
+ * use with non-ASCII scripts.</li>
+ * </ul>
*/
public class Time {
private static final String Y_M_D_T_H_M_S_000 = "%Y-%m-%dT%H:%M:%S.000";
@@ -106,7 +125,7 @@
public int isDst;
/**
- * Offset from UTC (in seconds).
+ * Offset in seconds from UTC including any DST offset.
*/
public long gmtoff;
@@ -137,41 +156,20 @@
public static final int FRIDAY = 5;
public static final int SATURDAY = 6;
- /*
- * The Locale for which date formatting strings have been loaded.
- */
- private static Locale sLocale;
- private static String[] sShortMonths;
- private static String[] sLongMonths;
- private static String[] sLongStandaloneMonths;
- private static String[] sShortWeekdays;
- private static String[] sLongWeekdays;
- private static String sTimeOnlyFormat;
- private static String sDateOnlyFormat;
- private static String sDateTimeFormat;
- private static String sAm;
- private static String sPm;
- private static char sZeroDigit;
-
- // Referenced by native code.
- private static String sDateCommand = "%a %b %e %H:%M:%S %Z %Y";
+ // An object that is reused for date calculations.
+ private TimeCalculator calculator;
/**
* Construct a Time object in the timezone named by the string
* argument "timezone". The time is initialized to Jan 1, 1970.
- * @param timezone string containing the timezone to use.
+ * @param timezoneId string containing the timezone to use.
* @see TimeZone
*/
- public Time(String timezone) {
- if (timezone == null) {
- throw new NullPointerException("timezone is null!");
+ public Time(String timezoneId) {
+ if (timezoneId == null) {
+ throw new NullPointerException("timezoneId is null!");
}
- this.timezone = timezone;
- this.year = 1970;
- this.monthDay = 1;
- // Set the daylight-saving indicator to the unknown value -1 so that
- // it will be recomputed.
- this.isDst = -1;
+ initialize(timezoneId);
}
/**
@@ -179,7 +177,7 @@
* Jan 1, 1970.
*/
public Time() {
- this(TimeZone.getDefault().getID());
+ initialize(TimeZone.getDefault().getID());
}
/**
@@ -189,9 +187,23 @@
* @param other
*/
public Time(Time other) {
+ initialize(other.timezone);
set(other);
}
+ /** Initialize the Time to 00:00:00 1/1/1970 in the specified timezone. */
+ private void initialize(String timezoneId) {
+ this.timezone = timezoneId;
+ this.year = 1970;
+ this.monthDay = 1;
+ // Set the daylight-saving indicator to the unknown value -1 so that
+ // it will be recomputed.
+ this.isDst = -1;
+
+ // A reusable object that performs the date/time calculations.
+ calculator = new TimeCalculator(timezoneId);
+ }
+
/**
* Ensures the values in each field are in range. For example if the
* current value of this calendar is March 32, normalize() will convert it
@@ -208,14 +220,26 @@
*
* @return the UTC milliseconds since the epoch
*/
- native public long normalize(boolean ignoreDst);
+ public long normalize(boolean ignoreDst) {
+ calculator.copyFieldsFromTime(this);
+ long timeInMillis = calculator.toMillis(ignoreDst);
+ calculator.copyFieldsToTime(this);
+ return timeInMillis;
+ }
/**
* Convert this time object so the time represented remains the same, but is
* instead located in a different timezone. This method automatically calls
- * normalize() in some cases
+ * normalize() in some cases.
+ *
+ * <p>This method can return incorrect results if the date / time cannot be normalized.
*/
- native public void switchTimezone(String timezone);
+ public void switchTimezone(String timezone) {
+ calculator.copyFieldsFromTime(this);
+ calculator.switchTimeZone(timezone);
+ calculator.copyFieldsToTime(this);
+ this.timezone = timezone;
+ }
private static final int[] DAYS_PER_MONTH = { 31, 28, 31, 30, 31, 30, 31,
31, 30, 31, 30, 31 };
@@ -265,13 +289,13 @@
/**
* Clears all values, setting the timezone to the given timezone. Sets isDst
* to a negative value to mean "unknown".
- * @param timezone the timezone to use.
+ * @param timezoneId the timezone to use.
*/
- public void clear(String timezone) {
- if (timezone == null) {
+ public void clear(String timezoneId) {
+ if (timezoneId == null) {
throw new NullPointerException("timezone is null!");
}
- this.timezone = timezone;
+ this.timezone = timezoneId;
this.allDay = false;
this.second = 0;
this.minute = 0;
@@ -304,12 +328,12 @@
} else if (b == null) {
throw new NullPointerException("b == null");
}
+ a.calculator.copyFieldsFromTime(a);
+ b.calculator.copyFieldsFromTime(b);
- return nativeCompare(a, b);
+ return TimeCalculator.compare(a.calculator, b.calculator);
}
- private static native int nativeCompare(Time a, Time b);
-
/**
* Print the current value given the format string provided. See man
* strftime for what means what. The final string must be less than 256
@@ -318,61 +342,21 @@
* @return a String containing the current time expressed in the current locale.
*/
public String format(String format) {
- synchronized (Time.class) {
- Locale locale = Locale.getDefault();
-
- if (sLocale == null || locale == null || !(locale.equals(sLocale))) {
- LocaleData localeData = LocaleData.get(locale);
-
- sAm = localeData.amPm[0];
- sPm = localeData.amPm[1];
- sZeroDigit = localeData.zeroDigit;
-
- sShortMonths = localeData.shortMonthNames;
- sLongMonths = localeData.longMonthNames;
- sLongStandaloneMonths = localeData.longStandAloneMonthNames;
- sShortWeekdays = localeData.shortWeekdayNames;
- sLongWeekdays = localeData.longWeekdayNames;
-
- Resources r = Resources.getSystem();
- sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
- sDateOnlyFormat = r.getString(com.android.internal.R.string.month_day_year);
- sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time);
-
- sLocale = locale;
- }
-
- String result = format1(format);
- if (sZeroDigit != '0') {
- result = localizeDigits(result);
- }
- return result;
- }
+ calculator.copyFieldsFromTime(this);
+ return calculator.format(format);
}
- native private String format1(String format);
-
- // TODO: unify this with java.util.Formatter's copy.
- private String localizeDigits(String s) {
- int length = s.length();
- int offsetToLocalizedDigits = sZeroDigit - '0';
- StringBuilder result = new StringBuilder(length);
- for (int i = 0; i < length; ++i) {
- char ch = s.charAt(i);
- if (ch >= '0' && ch <= '9') {
- ch += offsetToLocalizedDigits;
- }
- result.append(ch);
- }
- return result.toString();
- }
-
-
/**
* Return the current time in YYYYMMDDTHHMMSS<tz> format
*/
@Override
- native public String toString();
+ public String toString() {
+ // toString() uses its own TimeCalculator rather than the shared one. Otherwise crazy stuff
+ // happens during debugging when the debugger calls toString().
+ TimeCalculator calculator = new TimeCalculator(this.timezone);
+ calculator.copyFieldsFromTime(this);
+ return calculator.toStringInternal();
+ }
/**
* Parses a date-time string in either the RFC 2445 format or an abbreviated
@@ -414,7 +398,7 @@
if (s == null) {
throw new NullPointerException("time string is null");
}
- if (nativeParse(s)) {
+ if (parseInternal(s)) {
timezone = TIMEZONE_UTC;
return true;
}
@@ -424,7 +408,94 @@
/**
* Parse a time in the current zone in YYYYMMDDTHHMMSS format.
*/
- native private boolean nativeParse(String s);
+ private boolean parseInternal(String s) {
+ int len = s.length();
+ if (len < 8) {
+ throw new TimeFormatException("String is too short: \"" + s +
+ "\" Expected at least 8 characters.");
+ }
+
+ boolean inUtc = false;
+
+ // year
+ int n = getChar(s, 0, 1000);
+ n += getChar(s, 1, 100);
+ n += getChar(s, 2, 10);
+ n += getChar(s, 3, 1);
+ year = n;
+
+ // month
+ n = getChar(s, 4, 10);
+ n += getChar(s, 5, 1);
+ n--;
+ month = n;
+
+ // day of month
+ n = getChar(s, 6, 10);
+ n += getChar(s, 7, 1);
+ monthDay = n;
+
+ if (len > 8) {
+ if (len < 15) {
+ throw new TimeFormatException(
+ "String is too short: \"" + s
+ + "\" If there are more than 8 characters there must be at least"
+ + " 15.");
+ }
+ checkChar(s, 8, 'T');
+ allDay = false;
+
+ // hour
+ n = getChar(s, 9, 10);
+ n += getChar(s, 10, 1);
+ hour = n;
+
+ // min
+ n = getChar(s, 11, 10);
+ n += getChar(s, 12, 1);
+ minute = n;
+
+ // sec
+ n = getChar(s, 13, 10);
+ n += getChar(s, 14, 1);
+ second = n;
+
+ if (len > 15) {
+ // Z
+ checkChar(s, 15, 'Z');
+ inUtc = true;
+ }
+ } else {
+ allDay = true;
+ hour = 0;
+ minute = 0;
+ second = 0;
+ }
+
+ weekDay = 0;
+ yearDay = 0;
+ isDst = -1;
+ gmtoff = 0;
+ return inUtc;
+ }
+
+ private void checkChar(String s, int spos, char expected) {
+ char c = s.charAt(spos);
+ if (c != expected) {
+ throw new TimeFormatException(String.format(
+ "Unexpected character 0x%02d at pos=%d. Expected 0x%02d (\'%c\').",
+ (int) c, spos, (int) expected, expected));
+ }
+ }
+
+ private static int getChar(String s, int spos, int mul) {
+ char c = s.charAt(spos);
+ if (Character.isDigit(c)) {
+ return Character.getNumericValue(c) * mul;
+ } else {
+ throw new TimeFormatException("Parse error at pos=" + spos);
+ }
+ }
/**
* Parse a time in RFC 3339 format. This method also parses simple dates
@@ -461,14 +532,140 @@
if (s == null) {
throw new NullPointerException("time string is null");
}
- if (nativeParse3339(s)) {
+ if (parse3339Internal(s)) {
timezone = TIMEZONE_UTC;
return true;
}
return false;
}
- native private boolean nativeParse3339(String s);
+ private boolean parse3339Internal(String s) {
+ int len = s.length();
+ if (len < 10) {
+ throw new TimeFormatException("String too short --- expected at least 10 characters.");
+ }
+ boolean inUtc = false;
+
+ // year
+ int n = getChar(s, 0, 1000);
+ n += getChar(s, 1, 100);
+ n += getChar(s, 2, 10);
+ n += getChar(s, 3, 1);
+ year = n;
+
+ checkChar(s, 4, '-');
+
+ // month
+ n = getChar(s, 5, 10);
+ n += getChar(s, 6, 1);
+ --n;
+ month = n;
+
+ checkChar(s, 7, '-');
+
+ // day
+ n = getChar(s, 8, 10);
+ n += getChar(s, 9, 1);
+ monthDay = n;
+
+ if (len >= 19) {
+ // T
+ checkChar(s, 10, 'T');
+ allDay = false;
+
+ // hour
+ n = getChar(s, 11, 10);
+ n += getChar(s, 12, 1);
+
+ // Note that this.hour is not set here. It is set later.
+ int hour = n;
+
+ checkChar(s, 13, ':');
+
+ // minute
+ n = getChar(s, 14, 10);
+ n += getChar(s, 15, 1);
+ // Note that this.minute is not set here. It is set later.
+ int minute = n;
+
+ checkChar(s, 16, ':');
+
+ // second
+ n = getChar(s, 17, 10);
+ n += getChar(s, 18, 1);
+ second = n;
+
+ // skip the '.XYZ' -- we don't care about subsecond precision.
+
+ int tzIndex = 19;
+ if (tzIndex < len && s.charAt(tzIndex) == '.') {
+ do {
+ tzIndex++;
+ } while (tzIndex < len && Character.isDigit(s.charAt(tzIndex)));
+ }
+
+ int offset = 0;
+ if (len > tzIndex) {
+ char c = s.charAt(tzIndex);
+ // NOTE: the offset is meant to be subtracted to get from local time
+ // to UTC. we therefore use 1 for '-' and -1 for '+'.
+ switch (c) {
+ case 'Z':
+ // Zulu time -- UTC
+ offset = 0;
+ break;
+ case '-':
+ offset = 1;
+ break;
+ case '+':
+ offset = -1;
+ break;
+ default:
+ throw new TimeFormatException(String.format(
+ "Unexpected character 0x%02d at position %d. Expected + or -",
+ (int) c, tzIndex));
+ }
+ inUtc = true;
+
+ if (offset != 0) {
+ if (len < tzIndex + 6) {
+ throw new TimeFormatException(
+ String.format("Unexpected length; should be %d characters",
+ tzIndex + 6));
+ }
+
+ // hour
+ n = getChar(s, tzIndex + 1, 10);
+ n += getChar(s, tzIndex + 2, 1);
+ n *= offset;
+ hour += n;
+
+ // minute
+ n = getChar(s, tzIndex + 4, 10);
+ n += getChar(s, tzIndex + 5, 1);
+ n *= offset;
+ minute += n;
+ }
+ }
+ this.hour = hour;
+ this.minute = minute;
+
+ if (offset != 0) {
+ normalize(false);
+ }
+ } else {
+ allDay = true;
+ this.hour = 0;
+ this.minute = 0;
+ this.second = 0;
+ }
+
+ this.weekDay = 0;
+ this.yearDay = 0;
+ this.isDst = -1;
+ this.gmtoff = 0;
+ return inUtc;
+ }
/**
* Returns the timezone string that is currently set for the device.
@@ -480,7 +677,9 @@
/**
* Sets the time of the given Time object to the current time.
*/
- native public void setToNow();
+ public void setToNow() {
+ set(System.currentTimeMillis());
+ }
/**
* Converts this time to milliseconds. Suitable for interacting with the
@@ -530,7 +729,10 @@
* to read back the same milliseconds that you set with {@link #set(long)}
* or {@link #set(Time)} or after parsing a date string.
*/
- native public long toMillis(boolean ignoreDst);
+ public long toMillis(boolean ignoreDst) {
+ calculator.copyFieldsFromTime(this);
+ return calculator.toMillis(ignoreDst);
+ }
/**
* Sets the fields in this Time object given the UTC milliseconds. After
@@ -539,15 +741,23 @@
*
* @param millis the time in UTC milliseconds since the epoch.
*/
- native public void set(long millis);
+ public void set(long millis) {
+ allDay = false;
+ calculator.timezone = timezone;
+ calculator.setTimeInMillis(millis);
+ calculator.copyFieldsToTime(this);
+ }
/**
- * Format according to RFC 2445 DATETIME type.
+ * Format according to RFC 2445 DATE-TIME type.
*
- * <p>
- * The same as format("%Y%m%dT%H%M%S").
+ * <p>The same as format("%Y%m%dT%H%M%S"), or format("%Y%m%dT%H%M%SZ") for a Time with a
+ * timezone set to "UTC".
*/
- native public String format2445();
+ public String format2445() {
+ calculator.copyFieldsFromTime(this);
+ return calculator.format2445(!allDay);
+ }
/**
* Copy the value of that to this Time object. No normalization happens.
@@ -682,7 +892,6 @@
* Otherwise, if the timezone is UTC, expresses the time as Y-M-D-T-H-M-S UTC</p>
* <p>
* Otherwise the time is expressed the time as Y-M-D-T-H-M-S +- GMT</p>
- * @param allDay
* @return string in the RFC 3339 format.
*/
public String format3339(boolean allDay) {
@@ -693,7 +902,7 @@
} else {
String base = format(Y_M_D_T_H_M_S_000);
String sign = (gmtoff < 0) ? "-" : "+";
- int offset = (int)Math.abs(gmtoff);
+ int offset = (int) Math.abs(gmtoff);
int minutes = (offset % 3600) / 60;
int hours = offset / 3600;
@@ -714,16 +923,18 @@
}
/**
- * Computes the Julian day number, given the UTC milliseconds
- * and the offset (in seconds) from UTC. The Julian day for a given
- * date will be the same for every timezone. For example, the Julian
- * day for July 1, 2008 is 2454649. This is the same value no matter
- * what timezone is being used. The Julian day is useful for testing
- * if two events occur on the same day and for determining the relative
- * time of an event from the present ("yesterday", "3 days ago", etc.).
+ * Computes the Julian day number for a point in time in a particular
+ * timezone. The Julian day for a given date is the same for every
+ * timezone. For example, the Julian day for July 1, 2008 is 2454649.
*
- * <p>
- * Use {@link #toMillis(boolean)} to get the milliseconds.
+ * <p>Callers must pass the time in UTC millisecond (as can be returned
+ * by {@link #toMillis(boolean)} or {@link #normalize(boolean)})
+ * and the offset from UTC of the timezone in seconds (as might be in
+ * {@link #gmtoff}).
+ *
+ * <p>The Julian day is useful for testing if two events occur on the
+ * same calendar date and for determining the relative time of an event
+ * from the present ("yesterday", "3 days ago", etc.).
*
* @param millis the time in UTC milliseconds
* @param gmtoff the offset from UTC in seconds
@@ -810,4 +1021,240 @@
public static int getJulianMondayFromWeeksSinceEpoch(int week) {
return MONDAY_BEFORE_JULIAN_EPOCH + week * 7;
}
+
+ /**
+ * A class that handles date/time calculations.
+ *
+ * This class originated as a port of a native C++ class ("android.Time") to pure Java. It is
+ * separate from the enclosing class because some methods copy the result of calculations back
+ * to the enclosing object, but others do not: thus separate state is retained.
+ */
+ private static class TimeCalculator {
+ public final ZoneInfo.WallTime wallTime;
+ public String timezone;
+
+ // Information about the current timezone.
+ private ZoneInfo zoneInfo;
+
+ public TimeCalculator(String timezoneId) {
+ this.zoneInfo = lookupZoneInfo(timezoneId);
+ this.wallTime = new ZoneInfo.WallTime();
+ }
+
+ public long toMillis(boolean ignoreDst) {
+ if (ignoreDst) {
+ wallTime.setIsDst(-1);
+ }
+
+ int r = wallTime.mktime(zoneInfo);
+ if (r == -1) {
+ return -1;
+ }
+ return r * 1000L;
+ }
+
+ public void setTimeInMillis(long millis) {
+ // Preserve old 32-bit Android behavior.
+ int intSeconds = (int) (millis / 1000);
+
+ updateZoneInfoFromTimeZone();
+ wallTime.localtime(intSeconds, zoneInfo);
+ }
+
+ public String format(String format) {
+ if (format == null) {
+ format = "%c";
+ }
+ TimeFormatter formatter = new TimeFormatter();
+ return formatter.format(format, wallTime, zoneInfo);
+ }
+
+ private void updateZoneInfoFromTimeZone() {
+ if (!zoneInfo.getID().equals(timezone)) {
+ this.zoneInfo = lookupZoneInfo(timezone);
+ }
+ }
+
+ private static ZoneInfo lookupZoneInfo(String timezoneId) {
+ try {
+ ZoneInfo zoneInfo = ZoneInfoDB.getInstance().makeTimeZone(timezoneId);
+ if (zoneInfo == null) {
+ zoneInfo = ZoneInfoDB.getInstance().makeTimeZone("GMT");
+ }
+ if (zoneInfo == null) {
+ throw new AssertionError("GMT not found: \"" + timezoneId + "\"");
+ }
+ return zoneInfo;
+ } catch (IOException e) {
+ // This should not ever be thrown.
+ throw new AssertionError("Error loading timezone: \"" + timezoneId + "\"", e);
+ }
+ }
+
+ public void switchTimeZone(String timezone) {
+ int seconds = wallTime.mktime(zoneInfo);
+ this.timezone = timezone;
+ updateZoneInfoFromTimeZone();
+ wallTime.localtime(seconds, zoneInfo);
+ }
+
+ public String format2445(boolean hasTime) {
+ char[] buf = new char[hasTime ? 16 : 8];
+ int n = wallTime.getYear();
+
+ buf[0] = toChar(n / 1000);
+ n %= 1000;
+ buf[1] = toChar(n / 100);
+ n %= 100;
+ buf[2] = toChar(n / 10);
+ n %= 10;
+ buf[3] = toChar(n);
+
+ n = wallTime.getMonth() + 1;
+ buf[4] = toChar(n / 10);
+ buf[5] = toChar(n % 10);
+
+ n = wallTime.getMonthDay();
+ buf[6] = toChar(n / 10);
+ buf[7] = toChar(n % 10);
+
+ if (!hasTime) {
+ return new String(buf, 0, 8);
+ }
+
+ buf[8] = 'T';
+
+ n = wallTime.getHour();
+ buf[9] = toChar(n / 10);
+ buf[10] = toChar(n % 10);
+
+ n = wallTime.getMinute();
+ buf[11] = toChar(n / 10);
+ buf[12] = toChar(n % 10);
+
+ n = wallTime.getSecond();
+ buf[13] = toChar(n / 10);
+ buf[14] = toChar(n % 10);
+
+ if (TIMEZONE_UTC.equals(timezone)) {
+ // The letter 'Z' is appended to the end.
+ buf[15] = 'Z';
+ return new String(buf, 0, 16);
+ } else {
+ return new String(buf, 0, 15);
+ }
+ }
+
+ private char toChar(int n) {
+ return (n >= 0 && n <= 9) ? (char) (n + '0') : ' ';
+ }
+
+ /**
+ * A method that will return the state of this object in string form. Note: it has side
+ * effects and so has deliberately not been made the default {@link #toString()}.
+ */
+ public String toStringInternal() {
+ // This implementation possibly displays the un-normalized fields because that is
+ // what it has always done.
+ return String.format("%04d%02d%02dT%02d%02d%02d%s(%d,%d,%d,%d,%d)",
+ wallTime.getYear(),
+ wallTime.getMonth() + 1,
+ wallTime.getMonthDay(),
+ wallTime.getHour(),
+ wallTime.getMinute(),
+ wallTime.getSecond(),
+ timezone,
+ wallTime.getWeekDay(),
+ wallTime.getYearDay(),
+ wallTime.getGmtOffset(),
+ wallTime.getIsDst(),
+ toMillis(false /* use isDst */) / 1000
+ );
+
+ }
+
+ public static int compare(TimeCalculator aObject, TimeCalculator bObject) {
+ if (aObject.timezone.equals(bObject.timezone)) {
+ // If the timezones are the same, we can easily compare the two times.
+ int diff = aObject.wallTime.getYear() - bObject.wallTime.getYear();
+ if (diff != 0) {
+ return diff;
+ }
+
+ diff = aObject.wallTime.getMonth() - bObject.wallTime.getMonth();
+ if (diff != 0) {
+ return diff;
+ }
+
+ diff = aObject.wallTime.getMonthDay() - bObject.wallTime.getMonthDay();
+ if (diff != 0) {
+ return diff;
+ }
+
+ diff = aObject.wallTime.getHour() - bObject.wallTime.getHour();
+ if (diff != 0) {
+ return diff;
+ }
+
+ diff = aObject.wallTime.getMinute() - bObject.wallTime.getMinute();
+ if (diff != 0) {
+ return diff;
+ }
+
+ diff = aObject.wallTime.getSecond() - bObject.wallTime.getSecond();
+ if (diff != 0) {
+ return diff;
+ }
+
+ return 0;
+ } else {
+ // Otherwise, convert to milliseconds and compare that. This requires that object be
+ // normalized. Note: For dates that do not exist: toMillis() can return -1, which
+ // can be confused with a valid time.
+ long am = aObject.toMillis(false /* use isDst */);
+ long bm = bObject.toMillis(false /* use isDst */);
+ long diff = am - bm;
+ return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0);
+ }
+
+ }
+
+ public void copyFieldsToTime(Time time) {
+ time.second = wallTime.getSecond();
+ time.minute = wallTime.getMinute();
+ time.hour = wallTime.getHour();
+ time.monthDay = wallTime.getMonthDay();
+ time.month = wallTime.getMonth();
+ time.year = wallTime.getYear();
+
+ // Read-only fields that are derived from other information above.
+ time.weekDay = wallTime.getWeekDay();
+ time.yearDay = wallTime.getYearDay();
+
+ // < 0: DST status unknown, 0: is not in DST, 1: is in DST
+ time.isDst = wallTime.getIsDst();
+ // This is in seconds and includes any DST offset too.
+ time.gmtoff = wallTime.getGmtOffset();
+ }
+
+ public void copyFieldsFromTime(Time time) {
+ wallTime.setSecond(time.second);
+ wallTime.setMinute(time.minute);
+ wallTime.setHour(time.hour);
+ wallTime.setMonthDay(time.monthDay);
+ wallTime.setMonth(time.month);
+ wallTime.setYear(time.year);
+ wallTime.setWeekDay(time.weekDay);
+ wallTime.setYearDay(time.yearDay);
+ wallTime.setIsDst(time.isDst);
+ wallTime.setGmtOffset((int) time.gmtoff);
+
+ if (time.allDay && (time.second != 0 || time.minute != 0 || time.hour != 0)) {
+ throw new IllegalArgumentException("allDay is true but sec, min, hour are not 0.");
+ }
+
+ timezone = time.timezone;
+ updateZoneInfoFromTimeZone();
+ }
+ }
}
diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java
new file mode 100644
index 0000000..ec79b36
--- /dev/null
+++ b/core/java/android/text/format/TimeFormatter.java
@@ -0,0 +1,519 @@
+/*
+ * Based on the UCB version of strftime.c with the copyright notice appearing below.
+ */
+
+/*
+** Copyright (c) 1989 The Regents of the University of California.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms are permitted
+** provided that the above copyright notice and this paragraph are
+** duplicated in all such forms and that any documentation,
+** advertising materials, and other materials related to such
+** distribution and use acknowledge that the software was developed
+** by the University of California, Berkeley. The name of the
+** University may not be used to endorse or promote products derived
+** from this software without specific prior written permission.
+** THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+package android.text.format;
+
+import android.content.res.Resources;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Formatter;
+import java.util.Locale;
+import java.util.TimeZone;
+import libcore.icu.LocaleData;
+import libcore.util.ZoneInfo;
+
+/**
+ * Formatting logic for {@link Time}. Contains a port of Bionic's broken strftime_tz to Java. The
+ * main issue with this implementation is the treatment of characters as ASCII, despite returning
+ * localized (UTF-16) strings from the LocaleData.
+ *
+ * <p>This class is not thread safe.
+ */
+class TimeFormatter {
+ // An arbitrary value outside the range representable by a byte / ASCII character code.
+ private static final int FORCE_LOWER_CASE = 0x100;
+
+ private static final int SECSPERMIN = 60;
+ private static final int MINSPERHOUR = 60;
+ private static final int DAYSPERWEEK = 7;
+ private static final int MONSPERYEAR = 12;
+ private static final int HOURSPERDAY = 24;
+ private static final int DAYSPERLYEAR = 366;
+ private static final int DAYSPERNYEAR = 365;
+
+ /**
+ * The Locale for which the cached LocaleData and formats have been loaded.
+ */
+ private static Locale sLocale;
+ private static LocaleData sLocaleData;
+ private static String sTimeOnlyFormat;
+ private static String sDateOnlyFormat;
+ private static String sDateTimeFormat;
+
+ private final LocaleData localeData;
+ private final String dateTimeFormat;
+ private final String timeOnlyFormat;
+ private final String dateOnlyFormat;
+ private final Locale locale;
+
+ private StringBuilder outputBuilder;
+ private Formatter outputFormatter;
+
+ public TimeFormatter() {
+ synchronized (TimeFormatter.class) {
+ Locale locale = Locale.getDefault();
+
+ if (sLocale == null || !(locale.equals(sLocale))) {
+ sLocale = locale;
+ sLocaleData = LocaleData.get(locale);
+
+ Resources r = Resources.getSystem();
+ sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
+ sDateOnlyFormat = r.getString(com.android.internal.R.string.month_day_year);
+ sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time);
+ }
+
+ this.dateTimeFormat = sDateTimeFormat;
+ this.timeOnlyFormat = sTimeOnlyFormat;
+ this.dateOnlyFormat = sDateOnlyFormat;
+ this.locale = locale;
+ localeData = sLocaleData;
+ }
+ }
+
+ /**
+ * Format the specified {@code wallTime} using {@code pattern}. The output is returned.
+ */
+ public String format(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) {
+ try {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ outputBuilder = stringBuilder;
+ outputFormatter = new Formatter(stringBuilder, locale);
+
+ formatInternal(pattern, wallTime, zoneInfo);
+ String result = stringBuilder.toString();
+ // This behavior is the source of a bug since some formats are defined as being
+ // in ASCII. Generally localization is very broken.
+ if (localeData.zeroDigit != '0') {
+ result = localizeDigits(result);
+ }
+ return result;
+ } finally {
+ outputBuilder = null;
+ outputFormatter = null;
+ }
+ }
+
+ private String localizeDigits(String s) {
+ int length = s.length();
+ int offsetToLocalizedDigits = localeData.zeroDigit - '0';
+ StringBuilder result = new StringBuilder(length);
+ for (int i = 0; i < length; ++i) {
+ char ch = s.charAt(i);
+ if (ch >= '0' && ch <= '9') {
+ ch += offsetToLocalizedDigits;
+ }
+ result.append(ch);
+ }
+ return result.toString();
+ }
+
+ /**
+ * Format the specified {@code wallTime} using {@code pattern}. The output is written to
+ * {@link #outputBuilder}.
+ */
+ private void formatInternal(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) {
+ // Convert to ASCII bytes to be compatible with old implementation behavior.
+ byte[] bytes = pattern.getBytes(StandardCharsets.US_ASCII);
+ if (bytes.length == 0) {
+ return;
+ }
+
+ ByteBuffer formatBuffer = ByteBuffer.wrap(bytes);
+ while (formatBuffer.remaining() > 0) {
+ boolean outputCurrentByte = true;
+ char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
+ if (currentByteAsChar == '%') {
+ outputCurrentByte = handleToken(formatBuffer, wallTime, zoneInfo);
+ }
+ if (outputCurrentByte) {
+ currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
+ outputBuilder.append(currentByteAsChar);
+ }
+
+ formatBuffer.position(formatBuffer.position() + 1);
+ }
+ }
+
+ private boolean handleToken(ByteBuffer formatBuffer, ZoneInfo.WallTime wallTime,
+ ZoneInfo zoneInfo) {
+
+ // The byte at formatBuffer.position() is expected to be '%' at this point.
+ int modifier = 0;
+ while (formatBuffer.remaining() > 1) {
+ // Increment the position then get the new current byte.
+ formatBuffer.position(formatBuffer.position() + 1);
+ char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
+ switch (currentByteAsChar) {
+ case 'A':
+ modifyAndAppend((wallTime.getWeekDay() < 0
+ || wallTime.getWeekDay() >= DAYSPERWEEK)
+ ? "?" : localeData.longWeekdayNames[wallTime.getWeekDay() + 1],
+ modifier);
+ return false;
+ case 'a':
+ modifyAndAppend((wallTime.getWeekDay() < 0
+ || wallTime.getWeekDay() >= DAYSPERWEEK)
+ ? "?" : localeData.shortWeekdayNames[wallTime.getWeekDay() + 1],
+ modifier);
+ return false;
+ case 'B':
+ if (modifier == '-') {
+ modifyAndAppend((wallTime.getMonth() < 0
+ || wallTime.getMonth() >= MONSPERYEAR)
+ ? "?"
+ : localeData.longStandAloneMonthNames[wallTime.getMonth()],
+ modifier);
+ } else {
+ modifyAndAppend((wallTime.getMonth() < 0
+ || wallTime.getMonth() >= MONSPERYEAR)
+ ? "?" : localeData.longMonthNames[wallTime.getMonth()],
+ modifier);
+ }
+ return false;
+ case 'b':
+ case 'h':
+ modifyAndAppend((wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
+ ? "?" : localeData.shortMonthNames[wallTime.getMonth()],
+ modifier);
+ return false;
+ case 'C':
+ outputYear(wallTime.getYear(), true, false, modifier);
+ return false;
+ case 'c':
+ formatInternal(dateTimeFormat, wallTime, zoneInfo);
+ return false;
+ case 'D':
+ formatInternal("%m/%d/%y", wallTime, zoneInfo);
+ return false;
+ case 'd':
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ wallTime.getMonthDay());
+ return false;
+ case 'E':
+ case 'O':
+ // C99 locale modifiers are not supported.
+ continue;
+ case '_':
+ case '-':
+ case '0':
+ case '^':
+ case '#':
+ modifier = currentByteAsChar;
+ continue;
+ case 'e':
+ outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
+ wallTime.getMonthDay());
+ return false;
+ case 'F':
+ formatInternal("%Y-%m-%d", wallTime, zoneInfo);
+ return false;
+ case 'H':
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ wallTime.getHour());
+ return false;
+ case 'I':
+ int hour = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), hour);
+ return false;
+ case 'j':
+ int yearDay = wallTime.getYearDay() + 1;
+ outputFormatter.format(getFormat(modifier, "%03d", "%3d", "%d", "%03d"),
+ yearDay);
+ return false;
+ case 'k':
+ outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
+ wallTime.getHour());
+ return false;
+ case 'l':
+ int n2 = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
+ outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), n2);
+ return false;
+ case 'M':
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ wallTime.getMinute());
+ return false;
+ case 'm':
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ wallTime.getMonth() + 1);
+ return false;
+ case 'n':
+ modifyAndAppend("\n", modifier);
+ return false;
+ case 'p':
+ modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
+ : localeData.amPm[0], modifier);
+ return false;
+ case 'P':
+ modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
+ : localeData.amPm[0], FORCE_LOWER_CASE);
+ return false;
+ case 'R':
+ formatInternal("%H:%M", wallTime, zoneInfo);
+ return false;
+ case 'r':
+ formatInternal("%I:%M:%S %p", wallTime, zoneInfo);
+ return false;
+ case 'S':
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ wallTime.getSecond());
+ return false;
+ case 's':
+ int timeInSeconds = wallTime.mktime(zoneInfo);
+ modifyAndAppend(Integer.toString(timeInSeconds), modifier);
+ return false;
+ case 'T':
+ formatInternal("%H:%M:%S", wallTime, zoneInfo);
+ return false;
+ case 't':
+ modifyAndAppend("\t", modifier);
+ return false;
+ case 'U':
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ (wallTime.getYearDay() + DAYSPERWEEK - wallTime.getWeekDay())
+ / DAYSPERWEEK);
+ return false;
+ case 'u':
+ int day = (wallTime.getWeekDay() == 0) ? DAYSPERWEEK : wallTime.getWeekDay();
+ outputFormatter.format("%d", day);
+ return false;
+ case 'V': /* ISO 8601 week number */
+ case 'G': /* ISO 8601 year (four digits) */
+ case 'g': /* ISO 8601 year (two digits) */
+ {
+ int year = wallTime.getYear();
+ int yday = wallTime.getYearDay();
+ int wday = wallTime.getWeekDay();
+ int w;
+ while (true) {
+ int len = isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
+ // What yday (-3 ... 3) does the ISO year begin on?
+ int bot = ((yday + 11 - wday) % DAYSPERWEEK) - 3;
+ // What yday does the NEXT ISO year begin on?
+ int top = bot - (len % DAYSPERWEEK);
+ if (top < -3) {
+ top += DAYSPERWEEK;
+ }
+ top += len;
+ if (yday >= top) {
+ ++year;
+ w = 1;
+ break;
+ }
+ if (yday >= bot) {
+ w = 1 + ((yday - bot) / DAYSPERWEEK);
+ break;
+ }
+ --year;
+ yday += isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
+ }
+ if (currentByteAsChar == 'V') {
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), w);
+ } else if (currentByteAsChar == 'g') {
+ outputYear(year, false, true, modifier);
+ } else {
+ outputYear(year, true, true, modifier);
+ }
+ return false;
+ }
+ case 'v':
+ formatInternal("%e-%b-%Y", wallTime, zoneInfo);
+ return false;
+ case 'W':
+ int n = (wallTime.getYearDay() + DAYSPERWEEK - (
+ wallTime.getWeekDay() != 0 ? (wallTime.getWeekDay() - 1)
+ : (DAYSPERWEEK - 1))) / DAYSPERWEEK;
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
+ return false;
+ case 'w':
+ outputFormatter.format("%d", wallTime.getWeekDay());
+ return false;
+ case 'X':
+ formatInternal(timeOnlyFormat, wallTime, zoneInfo);
+ return false;
+ case 'x':
+ formatInternal(dateOnlyFormat, wallTime, zoneInfo);
+ return false;
+ case 'y':
+ outputYear(wallTime.getYear(), false, true, modifier);
+ return false;
+ case 'Y':
+ outputYear(wallTime.getYear(), true, true, modifier);
+ return false;
+ case 'Z':
+ if (wallTime.getIsDst() < 0) {
+ return false;
+ }
+ boolean isDst = wallTime.getIsDst() != 0;
+ modifyAndAppend(zoneInfo.getDisplayName(isDst, TimeZone.SHORT), modifier);
+ return false;
+ case 'z': {
+ if (wallTime.getIsDst() < 0) {
+ return false;
+ }
+ int diff = wallTime.getGmtOffset();
+ String sign;
+ if (diff < 0) {
+ sign = "-";
+ diff = -diff;
+ } else {
+ sign = "+";
+ }
+ modifyAndAppend(sign, modifier);
+ diff /= SECSPERMIN;
+ diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR);
+ outputFormatter.format(getFormat(modifier, "%04d", "%4d", "%d", "%04d"), diff);
+ return false;
+ }
+ case '+':
+ formatInternal("%a %b %e %H:%M:%S %Z %Y", wallTime, zoneInfo);
+ return false;
+ case '%':
+ // If conversion char is undefined, behavior is undefined. Print out the
+ // character itself.
+ default:
+ return true;
+ }
+ }
+ return true;
+ }
+
+ private void modifyAndAppend(CharSequence str, int modifier) {
+ switch (modifier) {
+ case FORCE_LOWER_CASE:
+ for (int i = 0; i < str.length(); i++) {
+ outputBuilder.append(brokenToLower(str.charAt(i)));
+ }
+ break;
+ case '^':
+ for (int i = 0; i < str.length(); i++) {
+ outputBuilder.append(brokenToUpper(str.charAt(i)));
+ }
+ break;
+ case '#':
+ for (int i = 0; i < str.length(); i++) {
+ char c = str.charAt(i);
+ if (brokenIsUpper(c)) {
+ c = brokenToLower(c);
+ } else if (brokenIsLower(c)) {
+ c = brokenToUpper(c);
+ }
+ outputBuilder.append(c);
+ }
+ break;
+ default:
+ outputBuilder.append(str);
+
+ }
+ }
+
+ private void outputYear(int value, boolean outputTop, boolean outputBottom, int modifier) {
+ int lead;
+ int trail;
+
+ final int DIVISOR = 100;
+ trail = value % DIVISOR;
+ lead = value / DIVISOR + trail / DIVISOR;
+ trail %= DIVISOR;
+ if (trail < 0 && lead > 0) {
+ trail += DIVISOR;
+ --lead;
+ } else if (lead < 0 && trail > 0) {
+ trail -= DIVISOR;
+ ++lead;
+ }
+ if (outputTop) {
+ if (lead == 0 && trail < 0) {
+ modifyAndAppend("-0", modifier);
+ } else {
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), lead);
+ }
+ }
+ if (outputBottom) {
+ int n = ((trail < 0) ? -trail : trail);
+ outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
+ }
+ }
+
+ private static String getFormat(int modifier, String normal, String underscore, String dash,
+ String zero) {
+ switch (modifier) {
+ case '_':
+ return underscore;
+ case '-':
+ return dash;
+ case '0':
+ return zero;
+ }
+ return normal;
+ }
+
+ private static boolean isLeap(int year) {
+ return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
+ }
+
+ /**
+ * A broken implementation of {@link Character#isUpperCase(char)} that assumes ASCII in order to
+ * be compatible with the old native implementation.
+ */
+ private static boolean brokenIsUpper(char toCheck) {
+ return toCheck >= 'A' && toCheck <= 'Z';
+ }
+
+ /**
+ * A broken implementation of {@link Character#isLowerCase(char)} that assumes ASCII in order to
+ * be compatible with the old native implementation.
+ */
+ private static boolean brokenIsLower(char toCheck) {
+ return toCheck >= 'a' && toCheck <= 'z';
+ }
+
+ /**
+ * A broken implementation of {@link Character#toLowerCase(char)} that assumes ASCII in order to
+ * be compatible with the old native implementation.
+ */
+ private static char brokenToLower(char input) {
+ if (input >= 'A' && input <= 'Z') {
+ return (char) (input - 'A' + 'a');
+ }
+ return input;
+ }
+
+ /**
+ * A broken implementation of {@link Character#toUpperCase(char)} that assumes ASCII in order to
+ * be compatible with the old native implementation.
+ */
+ private static char brokenToUpper(char input) {
+ if (input >= 'a' && input <= 'z') {
+ return (char) (input - 'a' + 'A');
+ }
+ return input;
+ }
+
+ /**
+ * Safely convert a byte containing an ASCII character to a char, even for character codes
+ * > 127.
+ */
+ private static char convertToChar(byte b) {
+ return (char) (b & 0xFF);
+ }
+}
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
index ce126ad..f3a48a6 100644
--- a/core/java/android/text/style/TtsSpan.java
+++ b/core/java/android/text/style/TtsSpan.java
@@ -119,7 +119,7 @@
/**
* The text associated with this span is a telephone number. The argument
- * {@link #ARG_NUMBER_PART} is required. {@link #ARG_COUNTRY_CODE} and
+ * {@link #ARG_NUMBER_PARTS} is required. {@link #ARG_COUNTRY_CODE} and
* {@link #ARG_EXTENSION} are optional.
* Also accepts the arguments {@link #ARG_GENDER},
* {@link #ARG_ANIMACY}, {@link #ARG_MULTIPLICITY} and
@@ -371,7 +371,7 @@
/**
* Argument used to specify the country code of a telephone number. Can be
- * a string of digits.
+ * a string of digits optionally prefixed with a "+".
* Can be used with {@link #TYPE_TELEPHONE}.
*/
public static final String ARG_COUNTRY_CODE = "android.arg.country_code";
@@ -382,7 +382,7 @@
* can be separated with a space, '-', '/' or '.'.
* Can be used with {@link #TYPE_TELEPHONE}.
*/
- public static final String ARG_NUMBER_PART = "android.arg.number_part";
+ public static final String ARG_NUMBER_PARTS = "android.arg.number_parts";
/**
* Argument used to specify the extension part of a telephone number. Can be
@@ -1224,6 +1224,214 @@
}
/**
+ * A builder for TtsSpans of type {@link TtsSpan #TYPE_MONEY}.
+ */
+ public static class MoneyBuilder
+ extends SemioticClassBuilder<MoneyBuilder> {
+
+ /**
+ * Creates a TtsSpan of type {@link TtsSpan#TYPE_MONEY}.
+ */
+ public MoneyBuilder() {
+ super(TtsSpan.TYPE_MONEY);
+ }
+
+ /**
+ * Convenience method that converts the number to a String and set it to
+ * the value for {@link TtsSpan#ARG_INTEGER_PART}.
+ * @param integerPart The integer part of the amount.
+ * @return This instance.
+ */
+ public MoneyBuilder setIntegerPart(long integerPart) {
+ return setIntegerPart(String.valueOf(integerPart));
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_INTEGER_PART} argument.
+ * @param integerPart A non-empty string of digits with an optional
+ * leading + or -.
+ * @return This instance.
+ */
+ public MoneyBuilder setIntegerPart(String integerPart) {
+ return setStringArgument(TtsSpan.ARG_INTEGER_PART, integerPart);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_FRACTIONAL_PART} argument.
+ * @param fractionalPart Can be a string of digits of any size.
+ * @return This instance.
+ */
+ public MoneyBuilder setFractionalPart(String fractionalPart) {
+ return setStringArgument(TtsSpan.ARG_FRACTIONAL_PART, fractionalPart);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_CURRENCY} argument.
+ * @param currency Should be a ISO4217 currency code, e.g. "USD".
+ * @return This instance.
+ */
+ public MoneyBuilder setCurrency(String currency) {
+ return setStringArgument(TtsSpan.ARG_CURRENCY, currency);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_QUANTITY} argument.
+ * @param quantity
+ * @return This instance.
+ */
+ public MoneyBuilder setQuantity(String quantity) {
+ return setStringArgument(TtsSpan.ARG_QUANTITY, quantity);
+ }
+ }
+
+ /**
+ * A builder for TtsSpans of type {@link TtsSpan #TYPE_TELEPHONE}.
+ */
+ public static class TelephoneBuilder
+ extends SemioticClassBuilder<TelephoneBuilder> {
+
+ /**
+ * Creates a TtsSpan of type {@link TtsSpan#TYPE_TELEPHONE}.
+ */
+ public TelephoneBuilder() {
+ super(TtsSpan.TYPE_TELEPHONE);
+ }
+
+ /**
+ * Creates a TtsSpan of type {@link TtsSpan#TYPE_TELEPHONE} and sets the
+ * {@link TtsSpan#ARG_NUMBER_PARTS} argument.
+ */
+ public TelephoneBuilder(String numberParts) {
+ this();
+ setNumberParts(numberParts);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_COUNTRY_CODE} argument.
+ * @param countryCode The country code can be a series of digits
+ * optionally prefixed with a "+".
+ * @return This instance.
+ */
+ public TelephoneBuilder setCountryCode(String countryCode) {
+ return setStringArgument(TtsSpan.ARG_COUNTRY_CODE, countryCode);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_NUMBER_PARTS} argument.
+ * @param numberParts The main telephone number. Can be a series of
+ * digits and letters separated by spaces, "/", "-" or ".".
+ * @return This instance.
+ */
+ public TelephoneBuilder setNumberParts(String numberParts) {
+ return setStringArgument(TtsSpan.ARG_NUMBER_PARTS, numberParts);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_EXTENSION} argument.
+ * @param extension The extension can be a series of digits.
+ * @return This instance.
+ */
+ public TelephoneBuilder setExtension(String extension) {
+ return setStringArgument(TtsSpan.ARG_EXTENSION, extension);
+ }
+ }
+
+ /**
+ * A builder for TtsSpans of type {@link TtsSpan #TYPE_ELECTRONIC}.
+ */
+ public static class ElectronicBuilder
+ extends SemioticClassBuilder<ElectronicBuilder> {
+
+ /**
+ * Creates a TtsSpan of type {@link TtsSpan#TYPE_ELECTRONIC}.
+ */
+ public ElectronicBuilder() {
+ super(TtsSpan.TYPE_ELECTRONIC);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_USERNAME} and {@link TtsSpan#ARG_DOMAIN}
+ * arguments, representing an email address.
+ * @param username The part before the @ in the email address.
+ * @param domain The part after the @ in the email address.
+ * @return This instance.
+ */
+ public ElectronicBuilder setEmailArguments(String username,
+ String domain) {
+ return setDomain(domain).setUsername(username);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_PROTOCOL} argument.
+ * @param protocol The protocol of the URI. Examples are "http" and
+ * "ftp".
+ * @return This instance.
+ */
+ public ElectronicBuilder setProtocol(String protocol) {
+ return setStringArgument(TtsSpan.ARG_PROTOCOL, protocol);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_USERNAME} argument.
+ * @return This instance.
+ */
+ public ElectronicBuilder setUsername(String username) {
+ return setStringArgument(TtsSpan.ARG_USERNAME, username);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_PASSWORD} argument.
+ * @return This instance.
+ */
+ public ElectronicBuilder setPassword(String password) {
+ return setStringArgument(TtsSpan.ARG_PASSWORD, password);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_DOMAIN} argument.
+ * @param domain The domain, for example "source.android.com".
+ * @return This instance.
+ */
+ public ElectronicBuilder setDomain(String domain) {
+ return setStringArgument(TtsSpan.ARG_DOMAIN, domain);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_PORT} argument.
+ * @return This instance.
+ */
+ public ElectronicBuilder setPort(int port) {
+ return setIntArgument(TtsSpan.ARG_PORT, port);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_PATH} argument.
+ * @param path For example "source/index.html".
+ * @return This instance.
+ */
+ public ElectronicBuilder setPath(String path) {
+ return setStringArgument(TtsSpan.ARG_PATH, path);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_QUERY_STRING} argument.
+ * @param queryString For example "arg=value&argtwo=value".
+ * @return This instance.
+ */
+ public ElectronicBuilder setQueryString(String queryString) {
+ return setStringArgument(TtsSpan.ARG_QUERY_STRING, queryString);
+ }
+
+ /**
+ * Sets the {@link TtsSpan#ARG_FRAGMENT_ID} argument.
+ * @return This instance.
+ */
+ public ElectronicBuilder setFragmentId(String fragmentId) {
+ return setStringArgument(TtsSpan.ARG_FRAGMENT_ID, fragmentId);
+ }
+ }
+
+ /**
* A builder for TtsSpans of type {@link TtsSpan #TYPE_DIGITS}.
*/
public static class DigitsBuilder
diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java
index 3f5e8e8..1550297 100644
--- a/core/java/android/transition/ChangeBounds.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -193,7 +193,8 @@
bottomRightAnimator = ObjectAnimator.ofInt(view, "right", "bottom",
bottomRightPath);
}
- Animator anim = mergeAnimators(topLeftAnimator, bottomRightAnimator);
+ Animator anim = TransitionUtils.mergeAnimators(topLeftAnimator,
+ bottomRightAnimator);
if (view.getParent() instanceof ViewGroup) {
final ViewGroup parent = (ViewGroup) view.getParent();
parent.suppressLayout(true);
@@ -256,7 +257,8 @@
clipAnimator = ObjectAnimator.ofObject(view, "clipBounds", sRectEvaluator,
tempStartBounds, tempEndBounds);
}
- Animator anim = mergeAnimators(translationAnimator, clipAnimator);
+ Animator anim = TransitionUtils.mergeAnimators(translationAnimator,
+ clipAnimator);
if (view.getParent() instanceof ViewGroup) {
final ViewGroup parent = (ViewGroup) view.getParent();
parent.suppressLayout(true);
@@ -330,16 +332,4 @@
}
return null;
}
-
- private static Animator mergeAnimators(Animator animator1, Animator animator2) {
- if (animator1 == null) {
- return animator2;
- } else if (animator2 == null) {
- return animator1;
- } else {
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(animator1, animator2);
- return animatorSet;
- }
- }
}
diff --git a/core/java/android/transition/ChangeImageTransform.java b/core/java/android/transition/ChangeImageTransform.java
index 12437d7..2b26756 100644
--- a/core/java/android/transition/ChangeImageTransform.java
+++ b/core/java/android/transition/ChangeImageTransform.java
@@ -32,10 +32,12 @@
import java.util.Map;
/**
- * Transitions changes in ImageView {@link ImageView#setScaleType(ImageView.ScaleType)} as
- * well as image scaling due to ImageView size changes. When combined with
- * {@link android.transition.ChangeBounds}, an ImageView that changes size will
- * scale smoothly.
+ * This Transition captures an ImageView's matrix before and after the
+ * scene change and animates it during the transition.
+ *
+ * <p>In combination with ChangeBounds, ChangeImageTransform allows ImageViews
+ * that change size, shape, or {@link android.widget.ImageView.ScaleType} to animate contents
+ * smoothly.</p>
*/
public class ChangeImageTransform extends Transition {
@@ -192,30 +194,8 @@
private ObjectAnimator createMatrixAnimator(final ImageView imageView, Matrix startMatrix,
final Matrix endMatrix) {
- ObjectAnimator animator = ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
+ return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
new MatrixEvaluator(), startMatrix, endMatrix);
- /*
- AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
- private Matrix mPausedMatrix;
-
- @Override
- public void onAnimationPause(Animator animation) {
- if (mPausedMatrix == null) {
- mPausedMatrix = new Matrix();
- }
- Matrix imageMatrix = imageView.getImageMatrix();
- mPausedMatrix.set(imageMatrix);
- imageView.animateTransform(endMatrix);
- }
-
- @Override
- public void onAnimationResume(Animator animation) {
- imageView.animateTransform(mPausedMatrix);
- }
- };
- animator.addPauseListener(listener);
- */
- return animator;
}
private static class MatrixEvaluator implements TypeEvaluator<Matrix> {
diff --git a/core/java/android/transition/ChangeScroll.java b/core/java/android/transition/ChangeScroll.java
new file mode 100644
index 0000000..39291bf
--- /dev/null
+++ b/core/java/android/transition/ChangeScroll.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.transition.Transition;
+import android.transition.TransitionValues;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * This transition captures the scroll properties of targets before and after
+ * the scene change and animates any changes.
+ *
+ * @hide
+ */
+public class ChangeScroll extends Transition {
+
+ private static final String PROPNAME_SCROLL_X = "android:changeScroll:x";
+ private static final String PROPNAME_SCROLL_Y = "android:changeScroll:y";
+
+ public ChangeScroll() {}
+
+ public ChangeScroll(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ private void captureValues(TransitionValues transitionValues) {
+ transitionValues.values.put(PROPNAME_SCROLL_X, transitionValues.view.getScrollX());
+ transitionValues.values.put(PROPNAME_SCROLL_Y, transitionValues.view.getScrollY());
+ }
+
+ @Override
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ final View view = endValues.view;
+ int startX = (Integer) startValues.values.get(PROPNAME_SCROLL_X);
+ int endX = (Integer) endValues.values.get(PROPNAME_SCROLL_X);
+ int startY = (Integer) startValues.values.get(PROPNAME_SCROLL_Y);
+ int endY = (Integer) endValues.values.get(PROPNAME_SCROLL_Y);
+ Animator scrollXAnimator = null;
+ Animator scrollYAnimator = null;
+ if (startX != endX) {
+ view.setScrollX(startX);
+ scrollXAnimator = ObjectAnimator.ofInt(view, "scrollX", startX, endX);
+ }
+ if (startY != endY) {
+ view.setScrollY(startY);
+ scrollYAnimator = ObjectAnimator.ofInt(view, "scrollY", startY, endY);
+ }
+ return TransitionUtils.mergeAnimators(scrollXAnimator, scrollYAnimator);
+ }
+}
diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java
index 420c248..1f9d093 100644
--- a/core/java/android/transition/Fade.java
+++ b/core/java/android/transition/Fade.java
@@ -69,17 +69,17 @@
* Fading mode used in {@link #Fade(int)} to make the transition
* operate on targets that are appearing. Maybe be combined with
* {@link #OUT} to fade both in and out. Equivalent to
- * {@link Visibility#IN}.
+ * {@link Visibility#MODE_IN}.
*/
- public static final int IN = Visibility.IN;
+ public static final int IN = Visibility.MODE_IN;
/**
* Fading mode used in {@link #Fade(int)} to make the transition
* operate on targets that are disappearing. Maybe be combined with
* {@link #IN} to fade both in and out. Equivalent to
- * {@link Visibility#OUT}.
+ * {@link Visibility#MODE_OUT}.
*/
- public static final int OUT = Visibility.OUT;
+ public static final int OUT = Visibility.MODE_OUT;
/**
* Constructs a Fade transition that will fade targets in and out.
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index 68b0a43..760ffe1 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -159,6 +159,8 @@
transition = new AutoTransition(mContext, attrs);
} else if ("recolor".equals(name)) {
transition = new Recolor(mContext, attrs);
+ } else if ("changeScroll".equals(name)) {
+ transition = new ChangeScroll(mContext, attrs);
} else if ("transitionSet".equals(name)) {
transition = new TransitionSet(mContext, attrs);
} else if ("transition".equals(name)) {
diff --git a/core/java/android/transition/TransitionUtils.java b/core/java/android/transition/TransitionUtils.java
new file mode 100644
index 0000000..931d46a
--- /dev/null
+++ b/core/java/android/transition/TransitionUtils.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+
+/**
+ * Static utility methods for Transitions.
+ *
+ * @hide
+ */
+public class TransitionUtils {
+
+ static Animator mergeAnimators(Animator animator1, Animator animator2) {
+ if (animator1 == null) {
+ return animator2;
+ } else if (animator2 == null) {
+ return animator1;
+ } else {
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(animator1, animator2);
+ return animatorSet;
+ }
+ }
+}
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index cf5ea4c..af2016c 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -50,16 +50,16 @@
/**
* Mode used in {@link #setMode(int)} to make the transition
* operate on targets that are appearing. Maybe be combined with
- * {@link #OUT} to target Visibility changes both in and out.
+ * {@link #MODE_OUT} to target Visibility changes both in and out.
*/
- public static final int IN = 0x1;
+ public static final int MODE_IN = 0x1;
/**
* Mode used in {@link #setMode(int)} to make the transition
* operate on targets that are disappearing. Maybe be combined with
- * {@link #IN} to target Visibility changes both in and out.
+ * {@link #MODE_IN} to target Visibility changes both in and out.
*/
- public static final int OUT = 0x2;
+ public static final int MODE_OUT = 0x2;
private static final String[] sTransitionProperties = {
PROPNAME_VISIBILITY,
@@ -76,7 +76,7 @@
ViewGroup endParent;
}
- private int mMode = IN | OUT;
+ private int mMode = MODE_IN | MODE_OUT;
private int mForcedStartVisibility = -1;
private int mForcedEndVisibility = -1;
@@ -98,12 +98,12 @@
* on <code>mode</code>.
*
* @param mode The behavior supported by this transition, a combination of
- * {@link #IN} and {@link #OUT}.
+ * {@link #MODE_IN} and {@link #MODE_OUT}.
* @attr ref android.R.styleable#VisibilityTransition_visibilityMode
*/
public void setMode(int mode) {
- if ((mode & ~(IN | OUT)) != 0) {
- throw new IllegalArgumentException("Only IN and OUT flags are allowed");
+ if ((mode & ~(MODE_IN | MODE_OUT)) != 0) {
+ throw new IllegalArgumentException("Only MODE_IN and MODE_OUT flags are allowed");
}
mMode = mode;
}
@@ -112,7 +112,7 @@
* Returns whether appearing and/or disappearing Views are supported.
*
* Returns whether appearing and/or disappearing Views are supported. A combination of
- * {@link #IN} and {@link #OUT}.
+ * {@link #MODE_IN} and {@link #MODE_OUT}.
* @attr ref android.R.styleable#VisibilityTransition_visibilityMode
*/
public int getMode() {
@@ -276,7 +276,7 @@
public Animator onAppear(ViewGroup sceneRoot,
TransitionValues startValues, int startVisibility,
TransitionValues endValues, int endVisibility) {
- if ((mMode & IN) != IN || endValues == null) {
+ if ((mMode & MODE_IN) != MODE_IN || endValues == null) {
return null;
}
return onAppear(sceneRoot, endValues.view, startValues, endValues);
@@ -339,7 +339,7 @@
public Animator onDisappear(ViewGroup sceneRoot,
TransitionValues startValues, int startVisibility,
TransitionValues endValues, int endVisibility) {
- if ((mMode & OUT) != OUT) {
+ if ((mMode & MODE_OUT) != MODE_OUT) {
return null;
}
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index f4a0448..c36421d 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -54,9 +54,11 @@
ArrayList<PathDataNode> list = new ArrayList<PathDataNode>();
while (end < pathData.length()) {
end = nextStart(pathData, end);
- String s = pathData.substring(start, end);
- float[] val = getFloats(s);
- addNode(list, s.charAt(0), val);
+ String s = pathData.substring(start, end).trim();
+ if (s.length() > 0) {
+ float[] val = getFloats(s);
+ addNode(list, s.charAt(0), val);
+ }
start = end;
end++;
@@ -135,6 +137,12 @@
list.add(new PathDataNode(cmd, val));
}
+ private static class ExtractFloatResult {
+ // We need to return the position of the next separator and whether the
+ // next float starts with a '-'.
+ int mEndPosition;
+ boolean mEndWithNegSign;
+ }
/**
* Parse the floats in the string.
@@ -148,42 +156,73 @@
return new float[0];
}
try {
- float[] tmp = new float[s.length()];
+ float[] results = new float[s.length()];
int count = 0;
- int pos = 1, end;
- while ((end = extract(s, pos)) >= 0) {
- if (pos < end) {
- tmp[count++] = Float.parseFloat(s.substring(pos, end));
+ int startPosition = 1;
+ int endPosition = 0;
+
+ ExtractFloatResult result = new ExtractFloatResult();
+ int totalLength = s.length();
+
+ // The startPosition should always be the first character of the
+ // current number, and endPosition is the character after the current
+ // number.
+ while (startPosition < totalLength) {
+ extract(s, startPosition, result);
+ endPosition = result.mEndPosition;
+
+ if (startPosition < endPosition) {
+ results[count++] = Float.parseFloat(
+ s.substring(startPosition, endPosition));
}
- pos = end + 1;
+
+ if (result.mEndWithNegSign) {
+ // Keep the '-' sign with next number.
+ startPosition = endPosition;
+ } else {
+ startPosition = endPosition + 1;
+ }
}
- // handle the final float if there is one
- if (pos < s.length()) {
- tmp[count++] = Float.parseFloat(s.substring(pos, s.length()));
- }
- return Arrays.copyOf(tmp, count);
- } catch (NumberFormatException e){
- Log.e(LOGTAG,"error in parsing \""+s+"\"");
+ return Arrays.copyOf(results, count);
+ } catch (NumberFormatException e) {
+ Log.e(LOGTAG, "error in parsing \"" + s + "\"");
throw e;
}
}
/**
- * Calculate the position of the next comma or space
+ * Calculate the position of the next comma or space or negative sign
* @param s the string to search
* @param start the position to start searching
- * @return the position of the next comma or space or -1 if none found
+ * @param result the result of the extraction, including the position of the
+ * the starting position of next number, whether it is ending with a '-'.
*/
- private static int extract(String s, int start) {
- int space = s.indexOf(' ', start);
- int comma = s.indexOf(',', start);
- if (space == -1) {
- return comma;
+ private static void extract(String s, int start, ExtractFloatResult result) {
+ // Now looking for ' ', ',' or '-' from the start.
+ int currentIndex = start;
+ boolean foundSeparator = false;
+ result.mEndWithNegSign = false;
+ for (; currentIndex < s.length(); currentIndex++) {
+ char currentChar = s.charAt(currentIndex);
+ switch (currentChar) {
+ case ' ':
+ case ',':
+ foundSeparator = true;
+ break;
+ case '-':
+ if (currentIndex != start) {
+ foundSeparator = true;
+ result.mEndWithNegSign = true;
+ }
+ break;
+ }
+ if (foundSeparator) {
+ break;
+ }
}
- if (comma == -1) {
- return space;
- }
- return (comma > space) ? space : comma;
+ // When there is nothing found, then we put the end position to the end
+ // of the string.
+ result.mEndPosition = currentIndex;
}
/**
diff --git a/core/java/android/util/TimeFormatException.java b/core/java/android/util/TimeFormatException.java
index d7a898b..f520523 100644
--- a/core/java/android/util/TimeFormatException.java
+++ b/core/java/android/util/TimeFormatException.java
@@ -18,7 +18,11 @@
public class TimeFormatException extends RuntimeException
{
- TimeFormatException(String s)
+
+ /**
+ * @hide
+ */
+ public TimeFormatException(String s)
{
super(s);
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index b17fa4a..154d227 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -831,4 +831,13 @@
return Integer.toString(state);
}
}
+
+ /**
+ * Returns true if display updates may be suspended while in the specified
+ * display power state.
+ * @hide
+ */
+ public static boolean isSuspendedState(int state) {
+ return state == STATE_OFF || state == STATE_DOZE_SUSPEND;
+ }
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index a61d771..5157c41 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -149,6 +149,12 @@
// boolean string as parsed by SystemProperties.getBoolean().
void setStrictModeVisualIndicatorPreference(String enabled);
+ /**
+ * Update the windowmanagers cached value of
+ * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled(null, userId)}
+ */
+ void updateScreenCaptureDisabled(int userId);
+
// These can only be called with the SET_ORIENTATION permission.
/**
* Update the current screen rotation based on the current state of
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 0a1204d..73c73218 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -346,7 +346,9 @@
* Deep copies the data into native to simplify reference ownership.
*/
public boolean setOutline(Outline outline) {
- if (outline == null || outline.isEmpty()) {
+ if (outline == null) {
+ return nSetOutlineNone(mNativeRenderNode);
+ } else if (outline.isEmpty()) {
return nSetOutlineEmpty(mNativeRenderNode);
} else if (outline.mRect != null) {
return nSetOutlineRoundRect(mNativeRenderNode, outline.mRect.left, outline.mRect.top,
@@ -878,6 +880,7 @@
int right, int bottom, float radius);
private static native boolean nSetOutlineConvexPath(long renderNode, long nativePath);
private static native boolean nSetOutlineEmpty(long renderNode);
+ private static native boolean nSetOutlineNone(long renderNode);
private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
private static native boolean nSetRevealClip(long renderNode,
boolean shouldClip, boolean inverseClip, float x, float y, float radius);
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index b033780..d14f226 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -88,8 +88,8 @@
private final float mLightY;
private final float mLightZ;
private final float mLightRadius;
- private final float mAmbientShadowAlpha;
- private final float mSpotShadowAlpha;
+ private final int mAmbientShadowAlpha;
+ private final int mSpotShadowAlpha;
private long mNativeProxy;
private boolean mInitialized = false;
@@ -104,8 +104,10 @@
mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
- mAmbientShadowAlpha = a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0);
- mSpotShadowAlpha = a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0);
+ mAmbientShadowAlpha = Math.round(
+ 255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0));
+ mSpotShadowAlpha = Math.round(
+ 255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0));
a.recycle();
long rootNodePtr = nCreateRootRenderNode();
@@ -208,7 +210,9 @@
mSurfaceHeight = height;
}
mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight);
- nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight, lightX, mLightY, mLightZ, mLightRadius);
+ nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight,
+ lightX, mLightY, mLightZ, mLightRadius,
+ mAmbientShadowAlpha, mSpotShadowAlpha);
}
@Override
@@ -453,7 +457,8 @@
private static native void nUpdateSurface(long nativeProxy, Surface window);
private static native void nPauseSurface(long nativeProxy, Surface window);
private static native void nSetup(long nativeProxy, int width, int height,
- float lightX, float lightY, float lightZ, float lightRadius);
+ float lightX, float lightY, float lightZ, float lightRadius,
+ int ambientShadowAlpha, int spotShadowAlpha);
private static native void nSetOpaque(long nativeProxy, boolean opaque);
private static native int nSyncAndDrawFrame(long nativeProxy,
long frameTimeNanos, long recordDuration, float density);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e138345..0079cc9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5048,8 +5048,17 @@
* View, false otherwise.
*
* @see ViewGroup#FOCUS_BLOCK_DESCENDANTS
+ * @see ViewGroup#getTouchscreenBlocksFocus()
*/
public boolean hasFocusable() {
+ if (!isFocusableInTouchMode()) {
+ for (ViewParent p = mParent; p instanceof ViewGroup; p = p.getParent()) {
+ final ViewGroup g = (ViewGroup) p;
+ if (g.shouldBlockFocusForTouchscreen()) {
+ return false;
+ }
+ }
+ }
return (mViewFlags & VISIBILITY_MASK) == VISIBLE && isFocusable();
}
@@ -7439,11 +7448,12 @@
* @return Whether any ancestor of this view blocks descendant focus.
*/
private boolean hasAncestorThatBlocksDescendantFocus() {
+ final boolean focusableInTouchMode = isFocusableInTouchMode();
ViewParent ancestor = mParent;
while (ancestor instanceof ViewGroup) {
final ViewGroup vgAncestor = (ViewGroup) ancestor;
if (vgAncestor.getDescendantFocusability() == ViewGroup.FOCUS_BLOCK_DESCENDANTS
- || vgAncestor.shouldBlockFocusForTouchscreen()) {
+ || (!focusableInTouchMode && vgAncestor.shouldBlockFocusForTouchscreen())) {
return true;
} else {
ancestor = vgAncestor.getParent();
@@ -15546,7 +15556,8 @@
}
/**
- * This function is called whenever the drawable hotspot changes.
+ * This function is called whenever the view hotspot changes and needs to
+ * be propagated to drawables managed by the view.
* <p>
* Be sure to call through to the superclass when overriding this function.
*
@@ -15888,30 +15899,6 @@
}
/**
- * Applies a tint to the background drawable.
- * <p>
- * Subsequent calls to {@link #setBackground(Drawable)} will automatically
- * mutate the drawable and apply the specified tint and tint mode using
- * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
- *
- * @param tint the tint to apply, may be {@code null} to clear tint
- * @param tintMode the blending mode used to apply the tint, may be
- * {@code null} to clear tint
- *
- * @attr ref android.R.styleable#View_backgroundTint
- * @attr ref android.R.styleable#View_backgroundTintMode
- * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
- */
- private void setBackgroundTint(@Nullable ColorStateList tint,
- @Nullable PorterDuff.Mode tintMode) {
- mBackgroundTint = tint;
- mBackgroundTintMode = tintMode;
- mHasBackgroundTint = true;
-
- applyBackgroundTint();
- }
-
- /**
* Applies a tint to the background drawable. Does not modify the current tint
* mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
* <p>
@@ -15922,16 +15909,20 @@
* @param tint the tint to apply, may be {@code null} to clear tint
*
* @attr ref android.R.styleable#View_backgroundTint
- * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+ * @see #getBackgroundTint()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setBackgroundTint(@Nullable ColorStateList tint) {
- setBackgroundTint(tint, mBackgroundTintMode);
+ mBackgroundTint = tint;
+ mHasBackgroundTint = true;
+
+ applyBackgroundTint();
}
/**
* @return the tint applied to the background drawable
* @attr ref android.R.styleable#View_backgroundTint
- * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+ * @see #setBackgroundTint(ColorStateList)
*/
@Nullable
public ColorStateList getBackgroundTint() {
@@ -15946,16 +15937,19 @@
* @param tintMode the blending mode used to apply the tint, may be
* {@code null} to clear tint
* @attr ref android.R.styleable#View_backgroundTintMode
- * @see #setBackgroundTint(ColorStateList)
+ * @see #getBackgroundTintMode()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
- setBackgroundTint(mBackgroundTint, tintMode);
+ mBackgroundTintMode = tintMode;
+
+ applyBackgroundTint();
}
/**
* @return the blending mode used to apply the tint to the background drawable
* @attr ref android.R.styleable#View_backgroundTintMode
- * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+ * @see #setBackgroundTintMode(PorterDuff.Mode)
*/
@Nullable
public PorterDuff.Mode getBackgroundTintMode() {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 04c8b0b..1028a0c 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -669,7 +669,7 @@
// shortcut: don't report a new focusable view if we block our descendants from
// getting focus
&& (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
- && !shouldBlockFocusForTouchscreen()
+ && (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())
// shortcut: don't report a new focusable view if we already are focused
// (and we don't prefer our descendants)
//
@@ -865,6 +865,17 @@
return mFocused;
}
+ View getDeepestFocusedChild() {
+ View v = this;
+ while (v != null) {
+ if (v.isFocused()) {
+ return v;
+ }
+ v = v instanceof ViewGroup ? ((ViewGroup) v).getFocusedChild() : null;
+ }
+ return null;
+ }
+
/**
* Returns true if this view has or contains focus
*
@@ -911,8 +922,7 @@
}
final int descendantFocusability = getDescendantFocusability();
- if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS &&
- !shouldBlockFocusForTouchscreen()) {
+ if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
final int count = mChildrenCount;
final View[] children = mChildren;
@@ -936,8 +946,11 @@
final int descendantFocusability = getDescendantFocusability();
- if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS &&
- !shouldBlockFocusForTouchscreen()) {
+ if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
+ if (shouldBlockFocusForTouchscreen()) {
+ focusableMode |= FOCUSABLES_TOUCH_MODE;
+ }
+
final int count = mChildrenCount;
final View[] children = mChildren;
@@ -955,7 +968,8 @@
// among the focusable children would be more interesting.
if ((descendantFocusability != FOCUS_AFTER_DESCENDANTS
// No focusable descendants
- || (focusableCount == views.size())) && !shouldBlockFocusForTouchscreen()) {
+ || (focusableCount == views.size())) &&
+ (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())) {
super.addFocusables(views, direction, focusableMode);
}
}
@@ -971,9 +985,12 @@
if (touchscreenBlocksFocus) {
mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
if (hasFocus()) {
- final View newFocus = focusSearch(FOCUS_FORWARD);
- if (newFocus != null) {
- newFocus.requestFocus();
+ final View focusedChild = getDeepestFocusedChild();
+ if (!focusedChild.isFocusableInTouchMode()) {
+ final View newFocus = focusSearch(FOCUS_FORWARD);
+ if (newFocus != null) {
+ newFocus.requestFocus();
+ }
}
}
} else {
@@ -2485,10 +2502,6 @@
}
int descendantFocusability = getDescendantFocusability();
- if (shouldBlockFocusForTouchscreen()) {
- return false;
- }
-
switch (descendantFocusability) {
case FOCUS_BLOCK_DESCENDANTS:
return super.requestFocus(direction, previouslyFocusedRect);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d9ad1f1..671aa10 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -468,13 +468,8 @@
// Compute surface insets required to draw at specified Z value.
// TODO: Use real shadow insets for a constant max Z.
- if (view.isHardwareAccelerated()) {
- final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
- attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
- } else {
- // Software accelerated windows can't use insets.
- attrs.surfaceInsets.setEmpty();
- }
+ final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
+ attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index 77d48e2..f796587 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -40,16 +40,14 @@
private final Object mLock = new Object();
- private final LongArray mTempLongArray = new LongArray();
-
private final SparseArray<AccessibilityWindowInfo> mWindowCache =
- new SparseArray<AccessibilityWindowInfo>();
+ new SparseArray<>();
private final SparseArray<LongSparseArray<AccessibilityNodeInfo>> mNodeCache =
- new SparseArray<LongSparseArray<AccessibilityNodeInfo>>();
+ new SparseArray<>();
private final SparseArray<AccessibilityWindowInfo> mTempWindowArray =
- new SparseArray<AccessibilityWindowInfo>();
+ new SparseArray<>();
public void addWindow(AccessibilityWindowInfo window) {
synchronized (mLock) {
@@ -183,7 +181,7 @@
sortedWindows.put(window.getLayer(), window);
}
- List<AccessibilityWindowInfo> windows = new ArrayList<AccessibilityWindowInfo>();
+ List<AccessibilityWindowInfo> windows = new ArrayList<>();
for (int i = windowCount - 1; i >= 0; i--) {
AccessibilityWindowInfo window = sortedWindows.valueAt(i);
windows.add(AccessibilityWindowInfo.obtain(window));
@@ -221,7 +219,7 @@
final int windowId = info.getWindowId();
LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
if (nodes == null) {
- nodes = new LongSparseArray<AccessibilityNodeInfo>();
+ nodes = new LongSparseArray<>();
mNodeCache.put(windowId, nodes);
}
@@ -233,23 +231,14 @@
// children have been removed to remove the descendants that
// are no longer present.
final LongArray newChildrenIds = info.getChildNodeIds();
- if (newChildrenIds != null) {
- // Cache the new ids as we will do some lookups.
- LongArray newChildNodeIds = mTempLongArray;
- final int newChildCount = newChildNodeIds.size();
- for (int i = 0; i < newChildCount; i++) {
- newChildNodeIds.add(newChildrenIds.get(i));
- }
- final int oldChildCount = oldInfo.getChildCount();
- for (int i = 0; i < oldChildCount; i++) {
- final long oldChildId = oldInfo.getChildId(i);
- if (newChildNodeIds.indexOf(oldChildId) < 0) {
- clearSubTreeLocked(windowId, oldChildId);
- }
+ final int oldChildCount = oldInfo.getChildCount();
+ for (int i = 0; i < oldChildCount; i++) {
+ final long oldChildId = oldInfo.getChildId(i);
+ // If the child is no longer present, remove the sub-tree.
+ if (newChildrenIds == null || newChildrenIds.indexOf(oldChildId) < 0) {
+ clearSubTreeLocked(windowId, oldChildId);
}
-
- newChildNodeIds.clear();
}
// Also be careful if the parent has changed since the new
@@ -397,7 +386,7 @@
continue;
}
- ArraySet<AccessibilityNodeInfo> seen = new ArraySet<AccessibilityNodeInfo>();
+ ArraySet<AccessibilityNodeInfo> seen = new ArraySet<>();
final int windowId = mNodeCache.keyAt(i);
final int nodeCount = nodes.size();
@@ -408,6 +397,8 @@
if (!seen.add(node)) {
Log.e(LOG_TAG, "Duplicate node: " + node
+ " in window:" + windowId);
+ // Stop now as we potentially found a loop.
+ continue;
}
// Check for one accessibility focus.
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index e1a7ba24..da99dd6 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -82,8 +82,7 @@
* @param accept whether the {@link WebView} instance should accept
* third party cookies
*/
- public synchronized void setAcceptThirdPartyCookies(WebView webview,
- boolean accept) {
+ public void setAcceptThirdPartyCookies(WebView webview, boolean accept) {
throw new MustOverrideException();
}
diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java
index 0a0507e..862e8c2 100644
--- a/core/java/android/webkit/PermissionRequest.java
+++ b/core/java/android/webkit/PermissionRequest.java
@@ -22,39 +22,41 @@
* This interface defines a permission request and is used when web content
* requests access to protected resources.
*
- * Either {@link #grant(long) grant()} or {@link #deny()} must be called in UI
+ * Either {@link #grant(String[]) grant()} or {@link #deny()} must be called in UI
* thread to respond to the request.
*/
-public interface PermissionRequest {
- /**
- * Resource belongs to geolocation service.
- */
- public final static long RESOURCE_GEOLOCATION = 1 << 0;
+public abstract class PermissionRequest {
/**
* Resource belongs to video capture device, like camera.
*/
- public final static long RESOURCE_VIDEO_CAPTURE = 1 << 1;
+ public final static String RESOURCE_VIDEO_CAPTURE = "android.webkit.resource.VIDEO_CAPTURE";
/**
* Resource belongs to audio capture device, like microphone.
*/
- public final static long RESOURCE_AUDIO_CAPTURE = 1 << 2;
+ public final static String RESOURCE_AUDIO_CAPTURE = "android.webkit.resource.AUDIO_CAPTURE";
/**
* Resource belongs to protected media identifier.
* After the user grants this resource, the origin can use EME APIs to generate the license
* requests.
*/
- public final static long RESOURCE_PROTECTED_MEDIA_ID = 1 << 3;
+ public final static String RESOURCE_PROTECTED_MEDIA_ID =
+ "android.webkit.resource.PROTECTED_MEDIA_ID";
/**
+ * Call this method to get the origin of the web page which is trying to access
+ * the restricted resources.
+ *
* @return the origin of web content which attempt to access the restricted
* resources.
*/
- public Uri getOrigin();
+ public abstract Uri getOrigin();
/**
- * @return a bit mask of resources the web content wants to access.
+ * Call this method to get the resources the web page is trying to access.
+ *
+ * @return the array of resources the web content wants to access.
*/
- public long getResources();
+ public abstract String[] getResources();
/**
* Call this method to grant origin the permission to access the given resources.
@@ -66,10 +68,10 @@
* This parameter is designed to avoid granting permission by accident
* especially when new resources are requested by web content.
*/
- public void grant(long resources);
+ public abstract void grant(String[] resources);
/**
* Call this method to deny the request.
*/
- public void deny();
+ public abstract void deny();
}
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 470d413..35c9598 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.net.Uri;
@@ -298,7 +299,7 @@
/**
* Notify the host application that web content is requesting permission to
* access the specified resources and the permission currently isn't granted
- * or denied. The host application must invoke {@link PermissionRequest#grant(long)}
+ * or denied. The host application must invoke {@link PermissionRequest#grant(String[])}
* or {@link PermissionRequest#deny()}.
*
* If this method isn't overridden, the permission is denied.
@@ -409,55 +410,93 @@
*
* @see FileChooserParams
*/
- public boolean showFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
+ public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
return false;
}
/**
- * Parameters used in the {@link #showFileChooser} method.
- * This is intended to be used as a read-only data struct by the application.
+ * UploadHelper simplifies file upload operations by providing helper methods that
+ * would handle most common file picker/media capture requests. The application
+ * can use the helper to build an intent to start a file picker, and then parse
+ * the result returned by the activity.
+ *
+ * How to use:
+ * 1. Create a helper using {@link FileChooserParams#getUploadHelper}
+ * 2. Build an intent using {@link UploadHelper#buildIntent}
+ * 3. Fire the intent using {@link android.app.Activity#startActivityForResult}.
+ * 4. Check for ActivityNotFoundException and take a user friendly action if thrown.
+ * 5. Listen the result using {@link android.app.Activity#onActivityResult}
+ * 6. Parse the result using {@link UploadHelper#parseResult}
+ * 7. Send the result using filePathCallback of {@link WebChromeClient#onShowFileChooser}
*/
- public static class FileChooserParams {
- // Flags for mode
- /** Bitflag for <code>mode</code> indicating multiple files maybe selected */
- public static final int MODE_OPEN_MULTIPLE = 1 << 0;
- /** Bitflag for <code>mode</code> indicating a folder maybe selected.
- * The implementation should enumerate all files selected by this operation */
- public static final int MODE_OPEN_FOLDER = 1 << 1;
- /** Bitflag for <code>mode</code> indicating a non-existant filename maybe returned */
- public static final int MODE_SAVE = 1 << 2;
+ public static abstract class UploadHelper {
+ /**
+ * Returns an intent that would start a file picker for file selection/media capture.
+ */
+ public abstract Intent buildIntent();
/**
- * Bit-field of the <code>MODE_</code> flags.
+ * Parses the result returned by the file picker activity.
*
- * 0 indicates plain single file open.
+ * @param resultCode the integer result code returned by the file picker activity.
+ * @param data the intent returned by the file picker activity.
+ * @return the Uris of selected file(s) or null if the resultCode indicates
+ * activity canceled or any other error.
*/
- public int mode;
+ public abstract Uri[] parseResult(int resultCode, Intent data);
+ }
+
+ /**
+ * Parameters used in the {@link #onShowFileChooser} method.
+ */
+ public static abstract class FileChooserParams {
+ /** Open single file. Requires that the file exists before allowing the user to pick it. */
+ public static final int OPEN = 0;
+ /** Like Open but allows multiple files to be selected. */
+ public static final int OPEN_MULTIPLE = 1;
+ /** Like Open but allows a folder to be selected. The implementation should enumerate
+ all files selected by this operation. */
+ public static final int OPEN_FOLDER = 2;
+ /** Allows picking a nonexistent file and saving it. */
+ public static final int SAVE = 3;
/**
- * Comma-seperated list of acceptable MIME types.
+ * Returns a helper to simplify choosing and uploading files. The helper builds a default
+ * intent that the application can send using startActivityForResult and processes the
+ * results.
*/
- public String acceptTypes;
+ public abstract UploadHelper getUploadHelper();
/**
- * true indicates a preference for a live media captured value (e.g. Camera, Microphone).
+ * Returns file chooser mode.
+ */
+ public abstract int getMode();
+
+ /**
+ * Returns an array of acceptable MIME types. The array will be empty if no
+ * acceptable types are specified.
+ */
+ public abstract String[] getAcceptTypes();
+
+ /**
+ * Returns preference for a live media captured value (e.g. Camera, Microphone).
+ * True indicates capture is enabled, false disabled.
*
- * Use <code>acceptTypes</code> to determine suitable capture devices.
+ * Use <code>getAcceptTypes</code> to determine suitable capture devices.
*/
- public boolean capture;
+ public abstract boolean isCaptureEnabled();
/**
- * The title to use for this file selector, or null.
- *
- * Maybe null, in which case a default title should be used.
+ * Returns the title to use for this file selector, or null. If null a default
+ * title should be used.
*/
- public String title;
+ public abstract CharSequence getTitle();
/**
- * Name of a default selection if appropriate, or null.
+ * The file path of a default selection if specified, or null.
*/
- public String defaultFilename;
+ public abstract String getDefaultFilename();
};
/**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 9cf3e4f..92703ab 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -88,7 +88,7 @@
* </pre>
* <p>See {@link android.content.Intent} for more information.</p>
*
- * <p>To provide a WebView in your own Activity, include a {@code <WebView>} in your layout,
+ * <p>To provide a WebView in your own Activity, include a {@code <WebView>} in your layout,
* or set the entire Activity window as a WebView during {@link
* android.app.Activity#onCreate(Bundle) onCreate()}:</p>
* <pre class="prettyprint">
@@ -1681,23 +1681,6 @@
}
/**
- * Preauthorize the given origin to access resources.
- * The authorization only valid for this WebView instance's life cycle and
- * will not retained.
- *
- * In the case that an origin has had resources preauthorized, calls to
- * {@link WebChromeClient#onPermissionRequest(PermissionRequest)} will not be
- * made for those resources from that origin.
- *
- * @param origin the origin authorized to access resources
- * @param resources the resource authorized to be accessed by origin.
- */
- public void preauthorizePermission(Uri origin, long resources) {
- checkThread();
- mProvider.preauthorizePermission(origin, resources);
- }
-
- /**
* Sets the Picture listener. This is an interface used to receive
* notifications of a new Picture.
*
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 13cd2bd..fe18138 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -248,8 +248,6 @@
public View findHierarchyView(String className, int hashCode);
- public void preauthorizePermission(Uri origin, long resources);
-
//-------------------------------------------------------------------------
// Provider internal methods
//-------------------------------------------------------------------------
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 7e2d809..be7e0bc 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -92,10 +92,11 @@
final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
setThumb(thumb);
+ mThumbTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.SeekBar_thumbTintMode, -1), mThumbTintMode);
+
if (a.hasValue(R.styleable.SeekBar_thumbTint)) {
mThumbTint = a.getColorStateList(R.styleable.SeekBar_thumbTint);
- mThumbTintMode = Drawable.parseTintMode(a.getInt(
- R.styleable.SeekBar_thumbTintMode, -1), mThumbTintMode);
mHasThumbTint = true;
applyThumbTint();
@@ -183,30 +184,6 @@
}
/**
- * Applies a tint to the thumb drawable.
- * <p>
- * Subsequent calls to {@link #setThumb(Drawable)} will automatically
- * mutate the drawable and apply the specified tint and tint mode using
- * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
- *
- * @param tint the tint to apply, may be {@code null} to clear tint
- * @param tintMode the blending mode used to apply the tint, may be
- * {@code null} to clear tint
- *
- * @attr ref android.R.styleable#SeekBar_thumbTint
- * @attr ref android.R.styleable#SeekBar_thumbTintMode
- * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
- */
- private void setThumbTint(@Nullable ColorStateList tint,
- @Nullable PorterDuff.Mode tintMode) {
- mThumbTint = tint;
- mThumbTintMode = tintMode;
- mHasThumbTint = true;
-
- applyThumbTint();
- }
-
- /**
* Applies a tint to the thumb drawable. Does not modify the current tint
* mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
* <p>
@@ -217,16 +194,20 @@
* @param tint the tint to apply, may be {@code null} to clear tint
*
* @attr ref android.R.styleable#SeekBar_thumbTint
- * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+ * @see #getThumbTint()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setThumbTint(@Nullable ColorStateList tint) {
- setThumbTint(tint, mThumbTintMode);
+ mThumbTint = tint;
+ mHasThumbTint = true;
+
+ applyThumbTint();
}
/**
* @return the tint applied to the thumb drawable
* @attr ref android.R.styleable#SeekBar_thumbTint
- * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+ * @see #setThumbTint(ColorStateList)
*/
@Nullable
public ColorStateList getThumbTint() {
@@ -240,17 +221,21 @@
*
* @param tintMode the blending mode used to apply the tint, may be
* {@code null} to clear tint
+ *
* @attr ref android.R.styleable#SeekBar_thumbTintMode
- * @see #setThumbTint(ColorStateList)
+ * @see #getThumbTintMode()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setThumbTintMode(@Nullable PorterDuff.Mode tintMode) {
- setThumbTint(mThumbTint, tintMode);
+ mThumbTintMode = tintMode;
+
+ applyThumbTint();
}
/**
* @return the blending mode used to apply the tint to the thumb drawable
* @attr ref android.R.styleable#SeekBar_thumbTintMode
- * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+ * @see #setThumbTintMode(PorterDuff.Mode)
*/
@Nullable
public PorterDuff.Mode getThumbTintMode() {
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 4aa2300..791151c 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -18,9 +18,12 @@
import com.android.internal.R;
+import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
@@ -41,8 +44,13 @@
*/
public class CheckedTextView extends TextView implements Checkable {
private boolean mChecked;
+
private int mCheckMarkResource;
private Drawable mCheckMarkDrawable;
+ private ColorStateList mCheckMarkTint = null;
+ private PorterDuff.Mode mCheckMarkTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasCheckMarkTint = false;
+
private int mBasePadding;
private int mCheckMarkWidth;
private boolean mNeedRequestlayout;
@@ -74,6 +82,16 @@
setCheckMarkDrawable(d);
}
+ mCheckMarkTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.CompoundButton_buttonTintMode, -1), mCheckMarkTintMode);
+
+ if (a.hasValue(R.styleable.CompoundButton_buttonTint)) {
+ mCheckMarkTint = a.getColorStateList(R.styleable.CompoundButton_buttonTint);
+ mHasCheckMarkTint = true;
+
+ applyCheckMarkTint();
+ }
+
boolean checked = a.getBoolean(R.styleable.CheckedTextView_checked, false);
setChecked(checked);
@@ -153,6 +171,7 @@
mCheckMarkWidth = d.getIntrinsicWidth();
d.setState(getDrawableState());
+ applyCheckMarkTint();
} else {
mCheckMarkWidth = 0;
}
@@ -163,6 +182,72 @@
resolvePadding();
}
+ /**
+ * Applies a tint to the check mark drawable. Does not modify the
+ * current tint mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * Subsequent calls to {@link #setCheckMarkDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and
+ * tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#CheckedTextView_checkMarkTint
+ * @see #getCheckMarkTint()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setCheckMarkTint(@Nullable ColorStateList tint) {
+ mCheckMarkTint = tint;
+ mHasCheckMarkTint = true;
+
+ applyCheckMarkTint();
+ }
+
+ /**
+ * @return the tint applied to the check mark drawable
+ * @attr ref android.R.styleable#CheckedTextView_checkMarkTint
+ * @see #setCheckMarkTint(ColorStateList)
+ */
+ @Nullable
+ public ColorStateList getCheckMarkTint() {
+ return mCheckMarkTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setCheckMarkTint(ColorStateList)} to the check mark
+ * drawable. The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#CheckedTextView_checkMarkTintMode
+ * @see #setCheckMarkTint(ColorStateList)
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setCheckMarkTintMode(@Nullable PorterDuff.Mode tintMode) {
+ mCheckMarkTintMode = tintMode;
+
+ applyCheckMarkTint();
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the check mark drawable
+ * @attr ref android.R.styleable#CheckedTextView_checkMarkTintMode
+ * @see #setCheckMarkTintMode(PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getCheckMarkTintMode() {
+ return mCheckMarkTintMode;
+ }
+
+ private void applyCheckMarkTint() {
+ if (mCheckMarkDrawable != null && mHasCheckMarkTint) {
+ mCheckMarkDrawable = mCheckMarkDrawable.mutate();
+ mCheckMarkDrawable.setTint(mCheckMarkTint, mCheckMarkTintMode);
+ }
+ }
+
@RemotableViewMethod
@Override
public void setVisibility(int visibility) {
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 9ba0fe1..a45777e 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -87,10 +87,11 @@
setButtonDrawable(d);
}
+ mButtonTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.CompoundButton_buttonTintMode, -1), mButtonTintMode);
+
if (a.hasValue(R.styleable.CompoundButton_buttonTint)) {
mButtonTint = a.getColorStateList(R.styleable.CompoundButton_buttonTint);
- mButtonTintMode = Drawable.parseTintMode(a.getInt(
- R.styleable.CompoundButton_buttonTintMode, -1), mButtonTintMode);
mHasButtonTint = true;
applyButtonTint();
@@ -238,52 +239,31 @@
}
/**
- * Applies a tint to the button drawable.
- * <p>
- * Subsequent calls to {@link #setButtonDrawable(Drawable)} will
- * automatically mutate the drawable and apply the specified tint and tint
- * mode using
- * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
- *
- * @param tint the tint to apply, may be {@code null} to clear tint
- * @param tintMode the blending mode used to apply the tint, may be
- * {@code null} to clear tint
- *
- * @attr ref android.R.styleable#CompoundButton_buttonTint
- * @attr ref android.R.styleable#CompoundButton_buttonTintMode
- * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
- */
- private void setButtonTint(@Nullable ColorStateList tint,
- @Nullable PorterDuff.Mode tintMode) {
- mButtonTint = tint;
- mButtonTintMode = tintMode;
- mHasButtonTint = true;
-
- applyButtonTint();
- }
-
- /**
* Applies a tint to the button drawable. Does not modify the current tint
* mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
* <p>
* Subsequent calls to {@link #setButtonDrawable(Drawable)} will
* automatically mutate the drawable and apply the specified tint and tint
* mode using
- * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
*
* @param tint the tint to apply, may be {@code null} to clear tint
*
* @attr ref android.R.styleable#CompoundButton_buttonTint
- * @see #setButtonTint(ColorStateList, android.graphics.PorterDuff.Mode)
+ * @see #setButtonTint(ColorStateList)
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setButtonTint(@Nullable ColorStateList tint) {
- setButtonTint(tint, mButtonTintMode);
+ mButtonTint = tint;
+ mHasButtonTint = true;
+
+ applyButtonTint();
}
/**
* @return the tint applied to the button drawable
* @attr ref android.R.styleable#CompoundButton_buttonTint
- * @see #setButtonTint(ColorStateList, PorterDuff.Mode)
+ * @see #setButtonTint(ColorStateList)
*/
@Nullable
public ColorStateList getButtonTint() {
@@ -298,16 +278,19 @@
* @param tintMode the blending mode used to apply the tint, may be
* {@code null} to clear tint
* @attr ref android.R.styleable#CompoundButton_buttonTintMode
- * @see #setButtonTint(ColorStateList)
+ * @see #getButtonTintMode()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setButtonTintMode(@Nullable PorterDuff.Mode tintMode) {
- setButtonTint(mButtonTint, tintMode);
+ mButtonTintMode = tintMode;
+
+ applyButtonTint();
}
/**
* @return the blending mode used to apply the tint to the button drawable
* @attr ref android.R.styleable#CompoundButton_buttonTintMode
- * @see #setButtonTint(ColorStateList, PorterDuff.Mode)
+ * @see #setButtonTintMode(PorterDuff.Mode)
*/
@Nullable
public PorterDuff.Mode getButtonTintMode() {
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 34f333e..81dfcbb 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -127,10 +127,11 @@
setMeasureAllChildren(true);
}
+ mForegroundTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.FrameLayout_foregroundTintMode, -1), mForegroundTintMode);
+
if (a.hasValue(R.styleable.FrameLayout_foregroundTint)) {
mForegroundTint = a.getColorStateList(R.styleable.FrameLayout_foregroundTint);
- mForegroundTintMode = Drawable.parseTintMode(a.getInt(
- R.styleable.FrameLayout_foregroundTintMode, -1), mForegroundTintMode);
mHasForegroundTint = true;
applyForegroundTint();
@@ -316,11 +317,6 @@
*/
private void setForegroundTint(@Nullable ColorStateList tint,
@Nullable PorterDuff.Mode tintMode) {
- mForegroundTint = tint;
- mForegroundTintMode = tintMode;
- mHasForegroundTint = true;
-
- applyForegroundTint();
}
/**
@@ -334,16 +330,20 @@
* @param tint the tint to apply, may be {@code null} to clear tint
*
* @attr ref android.R.styleable#FrameLayout_foregroundTint
- * @see #setForegroundTint(ColorStateList, PorterDuff.Mode)
+ * @see #getForegroundTint()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setForegroundTint(@Nullable ColorStateList tint) {
- setForegroundTint(tint, mForegroundTintMode);
+ mForegroundTint = tint;
+ mHasForegroundTint = true;
+
+ applyForegroundTint();
}
/**
* @return the tint applied to the foreground drawable
* @attr ref android.R.styleable#FrameLayout_foregroundTint
- * @see #setForegroundTint(ColorStateList, PorterDuff.Mode)
+ * @see #setForegroundTint(ColorStateList)
*/
@Nullable
public ColorStateList getForegroundTint() {
@@ -358,17 +358,20 @@
* @param tintMode the blending mode used to apply the tint, may be
* {@code null} to clear tint
* @attr ref android.R.styleable#FrameLayout_foregroundTintMode
- * @see #setForegroundTint(ColorStateList)
+ * @see #getForegroundTintMode()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
- setForegroundTint(mForegroundTint, tintMode);
+ mForegroundTintMode = tintMode;
+
+ applyForegroundTint();
}
/**
* @return the blending mode used to apply the tint to the foreground
* drawable
* @attr ref android.R.styleable#FrameLayout_foregroundTintMode
- * @see #setForegroundTint(ColorStateList, PorterDuff.Mode)
+ * @see #setForegroundTintMode(PorterDuff.Mode)
*/
@Nullable
public PorterDuff.Mode getForegroundTintMode() {
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index e97177d..0881f3e 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -168,10 +168,11 @@
setScaleType(sScaleTypeArray[index]);
}
+ mDrawableTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ImageView_tintMode, -1), mDrawableTintMode);
+
if (a.hasValue(R.styleable.ImageView_tint)) {
mDrawableTint = a.getColorStateList(R.styleable.ImageView_tint);
- mDrawableTintMode = Drawable.parseTintMode(a.getInt(
- R.styleable.ImageView_tintMode, -1), mDrawableTintMode);
mHasDrawableTint = true;
applyDrawableTint();
@@ -448,30 +449,6 @@
}
/**
- * Applies a tint to the image drawable.
- * <p>
- * Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically
- * mutate the drawable and apply the specified tint and tint mode using
- * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
- *
- * @param tint the tint to apply, may be {@code null} to clear tint
- * @param tintMode the blending mode used to apply the tint, may be
- * {@code null} to clear tint
- *
- * @attr ref android.R.styleable#ImageView_tint
- * @attr ref android.R.styleable#ImageView_tintMode
- * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
- */
- private void setTint(@Nullable ColorStateList tint,
- @Nullable PorterDuff.Mode tintMode) {
- mDrawableTint = tint;
- mDrawableTintMode = tintMode;
- mHasDrawableTint = true;
-
- applyDrawableTint();
- }
-
- /**
* Applies a tint to the image drawable. Does not modify the current tint
* mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
* <p>
@@ -482,16 +459,20 @@
* @param tint the tint to apply, may be {@code null} to clear tint
*
* @attr ref android.R.styleable#ImageView_tint
+ * @see #getTint()
* @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setTint(@Nullable ColorStateList tint) {
- setTint(tint, mDrawableTintMode);
+ mDrawableTint = tint;
+ mHasDrawableTint = true;
+
+ applyDrawableTint();
}
/**
* @return the tint applied to the image drawable
* @attr ref android.R.styleable#ImageView_tint
- * @see #setTint(ColorStateList, PorterDuff.Mode)
+ * @see #setTint(ColorStateList)
*/
@Nullable
public ColorStateList getTint() {
@@ -506,16 +487,19 @@
* @param tintMode the blending mode used to apply the tint, may be
* {@code null} to clear tint
* @attr ref android.R.styleable#ImageView_tintMode
- * @see #setTint(ColorStateList)
+ * @see #getTintMode()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setTintMode(@Nullable PorterDuff.Mode tintMode) {
- setTint(mDrawableTint, tintMode);
+ mDrawableTintMode = tintMode;
+
+ applyDrawableTint();
}
/**
* @return the blending mode used to apply the tint to the image drawable
* @attr ref android.R.styleable#ImageView_tintMode
- * @see #setTint(ColorStateList, PorterDuff.Mode)
+ * @see #setTintMode(PorterDuff.Mode)
*/
@Nullable
public PorterDuff.Mode getTintMode() {
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 84b213b..af4644b 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -1186,15 +1186,27 @@
/** Timeout before disallowing intercept on the source's parent. */
private final int mTapTimeout;
+ /** Timeout before accepting a long-press to start forwarding. */
+ private final int mLongPressTimeout;
+
/** Source view from which events are forwarded. */
private final View mSrc;
/** Runnable used to prevent conflicts with scrolling parents. */
private Runnable mDisallowIntercept;
+ /** Runnable used to trigger forwarding on long-press. */
+ private Runnable mTriggerLongPress;
+
/** Whether this listener is currently forwarding touch events. */
private boolean mForwarding;
+ /**
+ * Whether forwarding was initiated by a long-press. If so, we won't
+ * force the window to dismiss when the touch stream ends.
+ */
+ private boolean mWasLongPress;
+
/** The id of the first pointer down in the current event stream. */
private int mActivePointerId;
@@ -1203,6 +1215,9 @@
mScaledTouchSlop = ViewConfiguration.get(src.getContext()).getScaledTouchSlop();
mTapTimeout = ViewConfiguration.getTapTimeout();
+ // Use a medium-press timeout. Halfway between tap and long-press.
+ mLongPressTimeout = (mTapTimeout + ViewConfiguration.getLongPressTimeout()) / 2;
+
src.addOnAttachStateChangeListener(this);
}
@@ -1223,7 +1238,14 @@
final boolean wasForwarding = mForwarding;
final boolean forwarding;
if (wasForwarding) {
- forwarding = onTouchForwarded(event) || !onForwardingStopped();
+ if (mWasLongPress) {
+ // If we started forwarding as a result of a long-press,
+ // just silently stop forwarding events so that the window
+ // stays open.
+ forwarding = onTouchForwarded(event);
+ } else {
+ forwarding = onTouchForwarded(event) || !onForwardingStopped();
+ }
} else {
forwarding = onTouchObserved(event) && onForwardingStarted();
@@ -1305,21 +1327,29 @@
switch (actionMasked) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = srcEvent.getPointerId(0);
+ mWasLongPress = false;
+
if (mDisallowIntercept == null) {
mDisallowIntercept = new DisallowIntercept();
}
src.postDelayed(mDisallowIntercept, mTapTimeout);
+
+ if (mTriggerLongPress == null) {
+ mTriggerLongPress = new TriggerLongPress();
+ }
+ src.postDelayed(mTriggerLongPress, mLongPressTimeout);
break;
case MotionEvent.ACTION_MOVE:
final int activePointerIndex = srcEvent.findPointerIndex(mActivePointerId);
if (activePointerIndex >= 0) {
final float x = srcEvent.getX(activePointerIndex);
final float y = srcEvent.getY(activePointerIndex);
+
+ // Has the pointer has moved outside of the view?
if (!src.pointInView(x, y, mScaledTouchSlop)) {
- // The pointer has moved outside of the view.
- if (mDisallowIntercept != null) {
- src.removeCallbacks(mDisallowIntercept);
- }
+ clearCallbacks();
+
+ // Don't let the parent intercept our events.
src.getParent().requestDisallowInterceptTouchEvent(true);
return true;
}
@@ -1327,15 +1357,48 @@
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
- if (mDisallowIntercept != null) {
- src.removeCallbacks(mDisallowIntercept);
- }
+ clearCallbacks();
break;
}
return false;
}
+ private void clearCallbacks() {
+ if (mTriggerLongPress != null) {
+ mSrc.removeCallbacks(mTriggerLongPress);
+ }
+
+ if (mDisallowIntercept != null) {
+ mSrc.removeCallbacks(mDisallowIntercept);
+ }
+ }
+
+ private void onLongPress() {
+ clearCallbacks();
+
+ final View src = mSrc;
+ if (!src.isEnabled()) {
+ return;
+ }
+
+ if (!onForwardingStarted()) {
+ return;
+ }
+
+ // Don't let the parent intercept our events.
+ mSrc.getParent().requestDisallowInterceptTouchEvent(true);
+
+ // Make sure we cancel any ongoing source event stream.
+ final long now = SystemClock.uptimeMillis();
+ final MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0);
+ mSrc.onTouchEvent(e);
+ e.recycle();
+
+ mForwarding = true;
+ mWasLongPress = true;
+ }
+
/**
* Handled forwarded motion events and determines when to stop
* forwarding.
@@ -1379,6 +1442,13 @@
parent.requestDisallowInterceptTouchEvent(true);
}
}
+
+ private class TriggerLongPress implements Runnable {
+ @Override
+ public void run() {
+ onLongPress();
+ }
+ }
}
/**
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 394b255..20c1aa4 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -326,48 +326,48 @@
mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl);
+ mProgressTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ProgressBar_progressBackgroundTintMode, -1), mProgressTintMode);
+
if (a.hasValue(R.styleable.ProgressBar_progressTint)) {
mProgressTint = a.getColorStateList(
R.styleable.ProgressBar_progressTint);
- mProgressTintMode = Drawable.parseTintMode(a.getInt(
- R.styleable.ProgressBar_progressBackgroundTintMode, -1),
- mProgressTintMode);
mHasProgressTint = true;
applyProgressLayerTint(R.id.progress, mProgressTint,
mProgressTintMode, true);
}
+ mProgressBackgroundTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ProgressBar_progressTintMode, -1), mProgressBackgroundTintMode);
+
if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTint)) {
mProgressBackgroundTint = a.getColorStateList(
R.styleable.ProgressBar_progressBackgroundTint);
- mProgressBackgroundTintMode = Drawable.parseTintMode(a.getInt(
- R.styleable.ProgressBar_progressTintMode, -1),
- mProgressBackgroundTintMode);
mHasProgressBackgroundTint = true;
applyProgressLayerTint(R.id.background, mProgressBackgroundTint,
mProgressBackgroundTintMode, false);
}
+ mSecondaryProgressTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ProgressBar_secondaryProgressTintMode, -1), mSecondaryProgressTintMode);
+
if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTint)) {
mSecondaryProgressTint = a.getColorStateList(
R.styleable.ProgressBar_secondaryProgressTint);
- mSecondaryProgressTintMode = Drawable.parseTintMode(a.getInt(
- R.styleable.ProgressBar_secondaryProgressTintMode, -1),
- mSecondaryProgressTintMode);
mHasSecondaryProgressTint = true;
applyProgressLayerTint(R.id.secondaryProgress, mSecondaryProgressTint,
mSecondaryProgressTintMode, false);
}
+ mIndeterminateTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ProgressBar_indeterminateTintMode, -1), mIndeterminateTintMode);
+
if (a.hasValue(R.styleable.ProgressBar_indeterminateTint)) {
mIndeterminateTint = a.getColorStateList(
R.styleable.ProgressBar_indeterminateTint);
- mIndeterminateTintMode = Drawable.parseTintMode(a.getInt(
- R.styleable.ProgressBar_indeterminateTintMode, -1),
- mIndeterminateTintMode);
mHasIndeterminateTint = true;
applyIndeterminateTint();
@@ -574,31 +574,6 @@
}
/**
- * Applies a tint to the indeterminate drawable.
- * <p>
- * Subsequent calls to {@link #setVisibilminateDrawable(Drawable)} will
- * automatically mutate the drawable and apply the specified tint and
- * tint mode using
- * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
- *
- * @param tint the tint to apply, may be {@code null} to clear tint
- * @param tintMode the blending mode used to apply the tint, may be
- * {@code null} to clear tint
- *
- * @attr ref android.R.styleable#ProgressBar_indeterminateTint
- * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
- * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
- */
- private void setIndeterminateTint(@Nullable ColorStateList tint,
- @Nullable PorterDuff.Mode tintMode) {
- mIndeterminateTint = tint;
- mIndeterminateTintMode = tintMode;
- mHasIndeterminateTint = true;
-
- applyIndeterminateTint();
- }
-
- /**
* Applies a tint to the indeterminate drawable. Does not modify the
* current tint mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
* <p>
@@ -610,16 +585,20 @@
* @param tint the tint to apply, may be {@code null} to clear tint
*
* @attr ref android.R.styleable#ProgressBar_indeterminateTint
- * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+ * @see #getIndeterminateTint()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setIndeterminateTint(@Nullable ColorStateList tint) {
- setIndeterminateTint(tint, mIndeterminateTintMode);
+ mIndeterminateTint = tint;
+ mHasIndeterminateTint = true;
+
+ applyIndeterminateTint();
}
/**
* @return the tint applied to the indeterminate drawable
* @attr ref android.R.styleable#ProgressBar_indeterminateTint
- * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+ * @see #setIndeterminateTint(ColorStateList)
*/
@Nullable
public ColorStateList getIndeterminateTint() {
@@ -635,15 +614,18 @@
* {@code null} to clear tint
* @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
* @see #setIndeterminateTint(ColorStateList)
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) {
- setIndeterminateTint(mIndeterminateTint, tintMode);
+ mIndeterminateTintMode = tintMode;
+
+ applyIndeterminateTint();
}
/**
* @return the blending mode used to apply the tint to the indeterminate drawable
* @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
- * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+ * @see #setIndeterminateTintMode(PorterDuff.Mode)
*/
@Nullable
public PorterDuff.Mode getIndeterminateTintMode() {
@@ -749,36 +731,6 @@
/**
* Applies a tint to the progress indicator, if one exists, or to the
- * entire progress drawable otherwise.
- * <p>
- * The progress indicator should be specified as a layer with
- * id {@link android.R.id#progress} in a {@link LayerDrawable}
- * used as the progress drawable.
- * <p>
- * Subsequent calls to {@link #setProgressDrawable(Drawable)} will
- * automatically mutate the drawable and apply the specified tint and
- * tint mode using
- * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
- *
- * @param tint the tint to apply, may be {@code null} to clear tint
- * @param tintMode the blending mode used to apply the tint, may be
- * {@code null} to clear tint
- *
- * @attr ref android.R.styleable#ProgressBar_progressTint
- * @attr ref android.R.styleable#ProgressBar_progressTintMode
- * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
- */
- private void setProgressTint(@Nullable ColorStateList tint,
- @Nullable PorterDuff.Mode tintMode) {
- mProgressTint = tint;
- mProgressTintMode = tintMode;
- mHasProgressTint = true;
-
- applyProgressLayerTint(R.id.progress, tint, tintMode, true);
- }
-
- /**
- * Applies a tint to the progress indicator, if one exists, or to the
* entire progress drawable otherwise. Does not modify the current tint
* mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
* <p>
@@ -794,16 +746,20 @@
* @param tint the tint to apply, may be {@code null} to clear tint
*
* @attr ref android.R.styleable#ProgressBar_progressTint
- * @see #setProgressTint(ColorStateList)
+ * @see #getProgressTint()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setProgressTint(@Nullable ColorStateList tint) {
- setProgressTint(tint, mProgressTintMode);
+ mProgressTint = tint;
+ mHasProgressTint = true;
+
+ applyProgressLayerTint(R.id.progress, tint, mProgressTintMode, true);
}
/**
* @return the tint applied to the progress drawable
* @attr ref android.R.styleable#ProgressBar_progressTint
- * @see #setProgressTint(ColorStateList, PorterDuff.Mode)
+ * @see #setProgressTint(ColorStateList)
*/
@Nullable
public ColorStateList getProgressTint() {
@@ -818,16 +774,19 @@
* @param tintMode the blending mode used to apply the tint, may be
* {@code null} to clear tint
* @attr ref android.R.styleable#ProgressBar_progressTintMode
- * @see #setProgressTint(ColorStateList)
+ * @see #getProgressTintMode()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setProgressTintMode(@Nullable PorterDuff.Mode tintMode) {
- setProgressTint(mProgressTint, tintMode);
+ mProgressTintMode = tintMode;
+
+ applyProgressLayerTint(R.id.progress, mProgressTint, tintMode, true);
}
/**
* @return the blending mode used to apply the tint to the progress drawable
* @attr ref android.R.styleable#ProgressBar_progressTintMode
- * @see #setProgressTint(ColorStateList, PorterDuff.Mode)
+ * @see #setProgressTintMode(PorterDuff.Mode)
*/
@Nullable
public PorterDuff.Mode getProgressTintMode() {
@@ -835,35 +794,6 @@
}
/**
- * Applies a tint to the progress background, if one exists.
- * <p>
- * The progress background must be specified as a layer with
- * id {@link android.R.id#background} in a {@link LayerDrawable}
- * used as the progress drawable.
- * <p>
- * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
- * drawable contains a progress background will automatically mutate the
- * drawable and apply the specified tint and tint mode using
- * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
- *
- * @param tint the tint to apply, may be {@code null} to clear tint
- * @param tintMode the blending mode used to apply the tint, may be
- * {@code null} to clear tint
- *
- * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
- * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
- * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
- */
- private void setProgressBackgroundTint(@Nullable ColorStateList tint,
- @Nullable PorterDuff.Mode tintMode) {
- mProgressBackgroundTint = tint;
- mProgressBackgroundTintMode = tintMode;
- mHasProgressBackgroundTint = true;
-
- applyProgressLayerTint(R.id.background, tint, tintMode, false);
- }
-
- /**
* Applies a tint to the progress background, if one exists. Does not
* modify the current tint mode, which is
* {@link PorterDuff.Mode#SRC_ATOP} by default.
@@ -880,16 +810,20 @@
* @param tint the tint to apply, may be {@code null} to clear tint
*
* @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
- * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+ * @see #getProgressBackgroundTint()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setProgressBackgroundTint(@Nullable ColorStateList tint) {
- setProgressBackgroundTint(tint, mProgressBackgroundTintMode);
+ mProgressBackgroundTint = tint;
+ mHasProgressBackgroundTint = true;
+
+ applyProgressLayerTint(R.id.background, tint, mProgressBackgroundTintMode, false);
}
/**
* @return the tint applied to the progress background
* @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
- * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+ * @see #setProgressBackgroundTint(ColorStateList)
*/
@Nullable
public ColorStateList getProgressBackgroundTint() {
@@ -905,16 +839,19 @@
* {@code null} to clear tint
* @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
* @see #setProgressBackgroundTint(ColorStateList)
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
- setProgressBackgroundTint(mProgressBackgroundTint, tintMode);
+ mProgressBackgroundTintMode = tintMode;
+
+ applyProgressLayerTint(R.id.background, mProgressBackgroundTint, tintMode, false);
}
/**
* @return the blending mode used to apply the tint to the progress
* background
* @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
- * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+ * @see #setProgressBackgroundTintMode(PorterDuff.Mode)
*/
@Nullable
public PorterDuff.Mode getProgressBackgroundTintMode() {
@@ -923,35 +860,6 @@
/**
* Applies a tint to the secondary progress indicator, if one exists.
- * <p>
- * The secondary progress indicator must be specified as a layer with
- * id {@link android.R.id#secondaryProgress} in a {@link LayerDrawable}
- * used as the progress drawable.
- * <p>
- * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
- * drawable contains a secondary progress indicator will automatically
- * mutate the drawable and apply the specified tint and tint mode using
- * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
- *
- * @param tint the tint to apply, may be {@code null} to clear tint
- * @param tintMode the blending mode used to apply the tint, may be
- * {@code null} to clear tint
- *
- * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
- * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
- * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
- */
- private void setSecondaryProgressTint(@Nullable ColorStateList tint,
- @Nullable PorterDuff.Mode tintMode) {
- mSecondaryProgressTint = tint;
- mSecondaryProgressTintMode = tintMode;
- mHasSecondaryProgressTint = true;
-
- applyProgressLayerTint(R.id.secondaryProgress, tint, tintMode, false);
- }
-
- /**
- * Applies a tint to the secondary progress indicator, if one exists.
* Does not modify the current tint mode, which is
* {@link PorterDuff.Mode#SRC_ATOP} by default.
* <p>
@@ -967,16 +875,20 @@
* @param tint the tint to apply, may be {@code null} to clear tint
*
* @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
- * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+ * @see #getSecondaryProgressTint()
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setSecondaryProgressTint(@Nullable ColorStateList tint) {
- setSecondaryProgressTint(tint, mSecondaryProgressTintMode);
+ mSecondaryProgressTint = tint;
+ mHasSecondaryProgressTint = true;
+
+ applyProgressLayerTint(R.id.secondaryProgress, tint, mSecondaryProgressTintMode, false);
}
/**
* @return the tint applied to the secondary progress drawable
* @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
- * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+ * @see #setSecondaryProgressTint(ColorStateList)
*/
@Nullable
public ColorStateList getSecondaryProgressTint() {
@@ -993,16 +905,19 @@
* {@code null} to clear tint
* @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
* @see #setSecondaryProgressTint(ColorStateList)
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
*/
public void setSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) {
- setSecondaryProgressTint(mSecondaryProgressTint, tintMode);
+ mSecondaryProgressTintMode = tintMode;
+
+ applyProgressLayerTint(R.id.secondaryProgress, mSecondaryProgressTint, tintMode, false);
}
/**
* @return the blending mode used to apply the tint to the secondary
* progress drawable
* @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
- * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+ * @see #setSecondaryProgressTintMode(PorterDuff.Mode)
*/
@Nullable
public PorterDuff.Mode getSecondaryProgressTintMode() {
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 90e80d3..4dd7e07 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -195,6 +195,11 @@
LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END
};
+ /**
+ * Used to indicate left/right/top/bottom should be inferred from constraints
+ */
+ private static final int VALUE_NOT_SET = Integer.MIN_VALUE;
+
private View mBaselineView = null;
private boolean mHasBaselineAlignedChild;
@@ -670,8 +675,8 @@
/**
* Measure a child. The child should have left, top, right and bottom information
- * stored in its LayoutParams. If any of these values is -1 it means that the view
- * can extend up to the corresponding edge.
+ * stored in its LayoutParams. If any of these values is VALUE_NOT_SET it means
+ * that the view can extend up to the corresponding edge.
*
* @param child Child to measure
* @param params LayoutParams associated with child
@@ -744,11 +749,11 @@
int childSpecMode = 0;
int childSpecSize = 0;
- // Negative values in a mySize/myWidth/myWidth value in RelativeLayout
+ // Negative values in a mySize value in RelativeLayout
// measurement is code for, "we got an unspecified mode in the
// RelativeLayout's measure spec."
if (mySize < 0 && !mAllowBrokenMeasureSpecs) {
- if (childStart >= 0 && childEnd >= 0) {
+ if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
// Constraints fixed both edges, so child has an exact size.
childSpecSize = Math.max(0, childEnd - childStart);
childSpecMode = MeasureSpec.EXACTLY;
@@ -771,17 +776,17 @@
// If the view did not express a layout constraint for an edge, use
// view's margins and our padding
- if (tempStart < 0) {
+ if (tempStart == VALUE_NOT_SET) {
tempStart = startPadding + startMargin;
}
- if (tempEnd < 0) {
+ if (tempEnd == VALUE_NOT_SET) {
tempEnd = mySize - endPadding - endMargin;
}
// Figure out maximum size available to this view
int maxAvailable = tempEnd - tempStart;
- if (childStart >= 0 && childEnd >= 0) {
+ if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
// Constraints fixed both edges, so child must be an exact size
childSpecMode = MeasureSpec.EXACTLY;
childSpecSize = maxAvailable;
@@ -828,13 +833,13 @@
final int layoutDirection = getLayoutDirection();
int[] rules = params.getRules(layoutDirection);
- if (params.mLeft < 0 && params.mRight >= 0) {
+ if (params.mLeft == VALUE_NOT_SET && params.mRight != VALUE_NOT_SET) {
// Right is fixed, but left varies
params.mLeft = params.mRight - child.getMeasuredWidth();
- } else if (params.mLeft >= 0 && params.mRight < 0) {
+ } else if (params.mLeft != VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
// Left is fixed, but right varies
params.mRight = params.mLeft + child.getMeasuredWidth();
- } else if (params.mLeft < 0 && params.mRight < 0) {
+ } else if (params.mLeft == VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
// Both left and right vary
if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
if (!wrapContent) {
@@ -864,13 +869,13 @@
int[] rules = params.getRules();
- if (params.mTop < 0 && params.mBottom >= 0) {
+ if (params.mTop == VALUE_NOT_SET && params.mBottom != VALUE_NOT_SET) {
// Bottom is fixed, but top varies
params.mTop = params.mBottom - child.getMeasuredHeight();
- } else if (params.mTop >= 0 && params.mBottom < 0) {
+ } else if (params.mTop != VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
// Top is fixed, but bottom varies
params.mBottom = params.mTop + child.getMeasuredHeight();
- } else if (params.mTop < 0 && params.mBottom < 0) {
+ } else if (params.mTop == VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
// Both top and bottom vary
if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
if (!wrapContent) {
@@ -891,12 +896,14 @@
private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
RelativeLayout.LayoutParams anchorParams;
- // -1 indicated a "soft requirement" in that direction. For example:
- // left=10, right=-1 means the view must start at 10, but can go as far as it wants to the right
- // left =-1, right=10 means the view must end at 10, but can go as far as it wants to the left
+ // VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:
+ // left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it
+ // wants to the right
+ // left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it
+ // wants to the left
// left=10, right=20 means the left and right ends are both fixed
- childParams.mLeft = -1;
- childParams.mRight = -1;
+ childParams.mLeft = VALUE_NOT_SET;
+ childParams.mRight = VALUE_NOT_SET;
anchorParams = getRelatedViewParams(rules, LEFT_OF);
if (anchorParams != null) {
@@ -947,8 +954,8 @@
int[] rules = childParams.getRules();
RelativeLayout.LayoutParams anchorParams;
- childParams.mTop = -1;
- childParams.mBottom = -1;
+ childParams.mTop = VALUE_NOT_SET;
+ childParams.mBottom = VALUE_NOT_SET;
anchorParams = getRelatedViewParams(rules, ABOVE);
if (anchorParams != null) {
@@ -1222,9 +1229,6 @@
private int mLeft, mTop, mRight, mBottom;
- private int mStart = DEFAULT_MARGIN_RELATIVE;
- private int mEnd = DEFAULT_MARGIN_RELATIVE;
-
private boolean mRulesChanged = false;
private boolean mIsRtlCompatibilityMode = false;
@@ -1601,14 +1605,6 @@
@Override
public void resolveLayoutDirection(int layoutDirection) {
final boolean isLayoutRtl = isLayoutRtl();
- if (isLayoutRtl) {
- if (mStart != DEFAULT_MARGIN_RELATIVE) mRight = mStart;
- if (mEnd != DEFAULT_MARGIN_RELATIVE) mLeft = mEnd;
- } else {
- if (mStart != DEFAULT_MARGIN_RELATIVE) mLeft = mStart;
- if (mEnd != DEFAULT_MARGIN_RELATIVE) mRight = mEnd;
- }
-
if (hasRelativeRules() && layoutDirection != getLayoutDirection()) {
resolveRules(layoutDirection);
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index fac0eb2..30831cd 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -222,6 +222,7 @@
* @attr ref android.R.styleable#TextView_imeActionId
* @attr ref android.R.styleable#TextView_editorExtras
* @attr ref android.R.styleable#TextView_elegantTextHeight
+ * @attr ref android.R.styleable#TextView_letterSpacing
*/
@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
@@ -657,6 +658,7 @@
int shadowcolor = 0;
float dx = 0, dy = 0, r = 0;
boolean elegant = false;
+ float letterSpacing = 0;
final Resources.Theme theme = context.getTheme();
@@ -737,6 +739,10 @@
case com.android.internal.R.styleable.TextAppearance_elegantTextHeight:
elegant = appearance.getBoolean(attr, false);
break;
+
+ case com.android.internal.R.styleable.TextAppearance_letterSpacing:
+ letterSpacing = appearance.getFloat(attr, 0);
+ break;
}
}
@@ -1078,6 +1084,10 @@
case com.android.internal.R.styleable.TextView_elegantTextHeight:
elegant = a.getBoolean(attr, false);
break;
+
+ case com.android.internal.R.styleable.TextView_letterSpacing:
+ letterSpacing = a.getFloat(attr, 0);
+ break;
}
}
a.recycle();
@@ -1259,6 +1269,7 @@
}
setRawTextSize(textSize);
setElegantTextHeight(elegant);
+ setLetterSpacing(letterSpacing);
if (allCaps) {
setTransformationMethod(new AllCapsTransformationMethod(getContext()));
@@ -2487,6 +2498,11 @@
com.android.internal.R.styleable.TextAppearance_elegantTextHeight, false));
}
+ if (appearance.hasValue(com.android.internal.R.styleable.TextAppearance_letterSpacing)) {
+ setLetterSpacing(appearance.getFloat(
+ com.android.internal.R.styleable.TextAppearance_letterSpacing, 0));
+ }
+
appearance.recycle();
}
@@ -2667,6 +2683,41 @@
}
/**
+ * @return the extent by which text is currently being letter-spaced.
+ * This will normally be 0.
+ *
+ * @see #setLetterSpacing(float)
+ * @hide
+ */
+ public float getLetterSpacing() {
+ return mTextPaint.getLetterSpacing();
+ }
+
+ /**
+ * Sets text letter-spacing. The value is in 'EM' units. Typical values
+ * for slight expansion will be around 0.05. Negative values tighten text.
+ *
+ * @see #getLetterSpacing()
+ * @see Paint#setFlags
+ *
+ * @attr ref android.R.styleable#TextView_letterSpacing
+ * @hide
+ */
+ @android.view.RemotableViewMethod
+ public void setLetterSpacing(float letterSpacing) {
+ if (letterSpacing != mTextPaint.getLetterSpacing()) {
+ mTextPaint.setLetterSpacing(letterSpacing);
+
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
+ }
+ }
+
+
+ /**
* Sets the text color for all the states (normal, selected,
* focused) to be this color.
*
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 64d03be..71102e8 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -233,10 +233,11 @@
if (!TextUtils.isEmpty(subtitle)) {
setSubtitle(subtitle);
}
- a.recycle();
- mPopupContext = context;
- mPopupTheme = 0;
+ // Set the default context, since setPopupTheme() may be a no-op.
+ mPopupContext = mContext;
+ setPopupTheme(a.getResourceId(R.styleable.Toolbar_popupTheme, 0));
+ a.recycle();
}
/**
diff --git a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
index 5ab9217..6ed3bdc 100644
--- a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
+++ b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
@@ -58,7 +58,6 @@
Log.d("ExternalMediaFormatActivity", "onCreate!");
// Set up the "dialog"
final AlertController.AlertParams p = mAlertParams;
- p.mIconId = com.android.internal.R.drawable.stat_sys_warning;
p.mTitle = getString(com.android.internal.R.string.extmedia_format_title);
p.mMessage = getString(com.android.internal.R.string.extmedia_format_message);
p.mPositiveButtonText = getString(com.android.internal.R.string.extmedia_format_button_format);
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 901d6e6..5655506 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -27,6 +27,12 @@
// be kept in sync with frameworks/native/include/binder/IBatteryStats.h
void noteStartSensor(int uid, int sensor);
void noteStopSensor(int uid, int sensor);
+ void noteStartVideo(int uid);
+ void noteStopVideo(int uid);
+ void noteStartAudio(int uid);
+ void noteStopAudio(int uid);
+ void noteResetVideo();
+ void noteResetAudio();
// Remaining methods are only used in Java.
byte[] getStatistics();
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 01e5d40..0eadde1 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -16,24 +16,25 @@
package com.android.internal.app;
+import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
+
import android.app.Activity;
+import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.os.Bundle;
import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.UserInfo;
+import android.os.Process;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.app.ActivityManagerNative;
-import android.os.RemoteException;
import android.util.Slog;
+import android.widget.Toast;
import java.util.List;
import java.util.Set;
-
-
-
/*
* This is used in conjunction with the {@link setCrossProfileIntentFilter} method of
* {@link DevicePolicyManager} to enable intents to be passed in and out of a managed profile.
@@ -56,13 +57,17 @@
String className = intentReceived.getComponent().getClassName();
final UserHandle userDest;
+ final int userMessageId;
if (className.equals(FORWARD_INTENT_TO_USER_OWNER)) {
+ userMessageId = com.android.internal.R.string.forward_intent_to_owner;
userDest = UserHandle.OWNER;
} else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
+ userMessageId = com.android.internal.R.string.forward_intent_to_work;
userDest = getManagedProfile();
} else {
Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
+ userMessageId = -1;
userDest = null;
}
if (userDest == null) { // This covers the case where there is no managed profile.
@@ -85,7 +90,19 @@
}
if (canForward) {
newIntent.prepareToLeaveUser(callingUserId);
+
+ final android.content.pm.ResolveInfo ri = getPackageManager().resolveActivityAsUser(
+ newIntent, MATCH_DEFAULT_ONLY, userDest.getIdentifier());
+
+ // Only show a disclosure if this is a normal (non-OS) app
+ final boolean shouldShowDisclosure =
+ !UserHandle.isSameApp(ri.activityInfo.applicationInfo.uid, Process.SYSTEM_UID);
+
startActivityAsUser(newIntent, userDest);
+
+ if (shouldShowDisclosure) {
+ Toast.makeText(this, getString(userMessageId), Toast.LENGTH_LONG).show();
+ }
} else {
Slog.wtf(TAG, "the intent: " + newIntent + "cannot be forwarded from user "
+ callingUserId + " to user " + userDest.getIdentifier());
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index a866ca7..229df8f 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -36,7 +36,6 @@
import android.widget.TextView;
import java.text.Collator;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -106,22 +105,21 @@
return constructAdapter(context, layoutId, fieldId, false /* disable pseudolocales */);
}
- public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
- final int layoutId, final int fieldId, final boolean isInDeveloperMode) {
+ public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) {
final Resources resources = context.getResources();
- String[] locales = Resources.getSystem().getAssets().getLocales();
+ final String[] locales = Resources.getSystem().getAssets().getLocales();
List<String> localeList = new ArrayList<String>(locales.length);
Collections.addAll(localeList, locales);
if (isInDeveloperMode) {
if (!localeList.contains("zz_ZZ")) {
localeList.add("zz_ZZ");
}
- /** - TODO: Enable when zz_ZY Pseudolocale is complete
- * if (!localeList.contains("zz_ZY")) {
- * localeList.add("zz_ZY");
- * }
- */
+ /** - TODO: Enable when zz_ZY Pseudolocale is complete
+ * if (!localeList.contains("zz_ZY")) {
+ * localeList.add("zz_ZY");
+ * }
+ */
}
Collections.sort(localeList);
@@ -179,6 +177,13 @@
}
Collections.sort(localeInfos);
+ return localeInfos;
+ }
+
+ public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
+ final int layoutId, final int fieldId, final boolean isInDeveloperMode) {
+ final List<LocaleInfo> localeInfos = getAllAssetLocales(context, isInDeveloperMode);
+
final LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos) {
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index e1166f1..b951f50e 100644
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -87,7 +87,6 @@
final Intent intent = getIntent();
final AlertController.AlertParams p = mAlertParams;
Context context = getApplicationContext();
- p.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
p.mTitle = intent.getStringExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_TITLE);
p.mMessage = intent.getStringExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_MESSAGE);
p.mPositiveButtonText = String.format(context.getString(R.string.gpsVerifYes));
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index fdb305d..e215517 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -17,6 +17,9 @@
package com.android.internal.app;
import android.app.Activity;
+import android.app.usage.PackageUsageStats;
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
import android.os.AsyncTask;
import android.widget.AbsListView;
import android.widget.GridView;
@@ -54,12 +57,13 @@
import android.widget.TextView;
import com.android.internal.widget.ResolverDrawerLayout;
+import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.Set;
/**
@@ -69,7 +73,7 @@
*/
public class ResolverActivity extends Activity implements AdapterView.OnItemClickListener {
private static final String TAG = "ResolverActivity";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private int mLaunchedFromUid;
private ResolveListAdapter mAdapter;
@@ -84,6 +88,10 @@
private int mMaxColumns;
private int mLastSelected = ListView.INVALID_POSITION;
+ private UsageStatsManager mUsm;
+ private UsageStats mStats;
+ private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
+
private boolean mRegistered;
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override public void onSomePackagesChanged() {
@@ -170,7 +178,7 @@
protected void onCreate(Bundle savedInstanceState, Intent intent,
CharSequence title, Intent[] initialIntents,
List<ResolveInfo> rList, boolean alwaysUseOption) {
- onCreate(savedInstanceState, intent, title, initialIntents, rList, alwaysUseOption);
+ onCreate(savedInstanceState, intent, title, 0, initialIntents, rList, alwaysUseOption);
}
protected void onCreate(Bundle savedInstanceState, Intent intent,
@@ -185,6 +193,12 @@
mLaunchedFromUid = -1;
}
mPm = getPackageManager();
+ mUsm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
+
+ final long sinceTime = System.currentTimeMillis() - USAGE_STATS_PERIOD;
+ mStats = mUsm.getRecentStatsSince(sinceTime);
+ Log.d(TAG, "sinceTime=" + sinceTime);
+
mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns);
mPackageMonitor.register(this, getMainLooper(), false);
@@ -672,7 +686,7 @@
for (int i=1; i<N; i++) {
ResolveInfo ri = currentResolveList.get(i);
if (DEBUG) Log.v(
- "ResolveListActivity",
+ TAG,
r0.activityInfo.name + "=" +
r0.priority + "/" + r0.isDefault + " vs " +
ri.activityInfo.name + "=" +
@@ -689,8 +703,8 @@
}
}
if (N > 1) {
- ResolveInfo.DisplayNameComparator rComparator =
- new ResolveInfo.DisplayNameComparator(mPm);
+ Comparator<ResolveInfo> rComparator =
+ new ResolverComparator(ResolverActivity.this);
Collections.sort(currentResolveList, rComparator);
}
// First put the initial items at the top.
@@ -703,8 +717,7 @@
ActivityInfo ai = ii.resolveActivityInfo(
getPackageManager(), 0);
if (ai == null) {
- Log.w("ResolverActivity", "No activity found for "
- + ii);
+ Log.w(TAG, "No activity found for " + ii);
continue;
}
ResolveInfo ri = new ResolveInfo();
@@ -939,5 +952,52 @@
mTargetView.setImageDrawable(info.displayIcon);
}
}
+
+ class ResolverComparator implements Comparator<ResolveInfo> {
+ private final Collator mCollator;
+
+ public ResolverComparator(Context context) {
+ mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
+ }
+
+ @Override
+ public int compare(ResolveInfo lhs, ResolveInfo rhs) {
+ // We want to put the one targeted to another user at the end of the dialog.
+ if (lhs.targetUserId != UserHandle.USER_CURRENT) {
+ return 1;
+ }
+ if (lhs.targetUserId != UserHandle.USER_CURRENT) {
+ return -1;
+ }
+
+ if (mStats != null) {
+ final long timeDiff =
+ getPackageTimeSpent(rhs.activityInfo.packageName) -
+ getPackageTimeSpent(lhs.activityInfo.packageName);
+
+ if (timeDiff != 0) {
+ return timeDiff > 0 ? 1 : -1;
+ }
+ }
+
+ CharSequence sa = lhs.loadLabel(mPm);
+ if (sa == null) sa = lhs.activityInfo.name;
+ CharSequence sb = rhs.loadLabel(mPm);
+ if (sb == null) sb = rhs.activityInfo.name;
+
+ return mCollator.compare(sa.toString(), sb.toString());
+ }
+
+ private long getPackageTimeSpent(String packageName) {
+ if (mStats != null) {
+ final PackageUsageStats stats = mStats.getPackage(packageName);
+ if (stats != null) {
+ return stats.getTotalTimeSpent();
+ }
+
+ }
+ return 0;
+ }
+ }
}
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 73d3738..c552a41 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -73,6 +73,7 @@
public PendingIntent configureIntent;
public long startTime = -1;
public boolean legacy;
+ public boolean blocking;
public void addLegacyRoutes(String routesStr) {
if (routesStr.trim().equals("")) {
@@ -120,6 +121,7 @@
out.writeParcelable(configureIntent, flags);
out.writeLong(startTime);
out.writeInt(legacy ? 1 : 0);
+ out.writeInt(blocking ? 1 : 0);
}
public static final Parcelable.Creator<VpnConfig> CREATOR =
@@ -138,6 +140,7 @@
config.configureIntent = in.readParcelable(null);
config.startTime = in.readLong();
config.legacy = in.readInt() != 0;
+ config.blocking = in.readInt() != 0;
return config;
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index dec94f2..aad1156 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -172,6 +172,8 @@
final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<StopwatchTimer>();
final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers =
new SparseArray<ArrayList<StopwatchTimer>>();
+ final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<StopwatchTimer>();
+ final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<StopwatchTimer>();
// Last partial timers we use for distributing CPU usage.
final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<StopwatchTimer>();
@@ -256,10 +258,10 @@
boolean mPhoneOn;
StopwatchTimer mPhoneOnTimer;
- boolean mAudioOn;
+ int mAudioOnNesting;
StopwatchTimer mAudioOnTimer;
- boolean mVideoOn;
+ int mVideoOnNesting;
StopwatchTimer mVideoOnTimer;
boolean mFlashlightOn;
@@ -1472,6 +1474,13 @@
}
}
+ void stopAllRunningLocked(long elapsedRealtimeMs) {
+ if (mNesting > 0) {
+ mNesting = 1;
+ stopRunningLocked(elapsedRealtimeMs);
+ }
+ }
+
// Update the total time for all other running Timers with the same type as this Timer
// due to a change in timer count
private static long refreshTimersLocked(long batteryRealtime,
@@ -3187,27 +3196,29 @@
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
- if (!mAudioOn) {
+ if (mAudioOnNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_AUDIO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
- mAudioOn = true;
mAudioOnTimer.startRunningLocked(elapsedRealtime);
}
+ mAudioOnNesting++;
getUidStatsLocked(uid).noteAudioTurnedOnLocked(elapsedRealtime);
}
public void noteAudioOffLocked(int uid) {
+ if (mAudioOnNesting == 0) {
+ return;
+ }
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
- if (mAudioOn) {
+ if (--mAudioOnNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
- mAudioOn = false;
mAudioOnTimer.stopRunningLocked(elapsedRealtime);
}
getUidStatsLocked(uid).noteAudioTurnedOffLocked(elapsedRealtime);
@@ -3217,32 +3228,68 @@
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
- if (!mVideoOn) {
+ if (mVideoOnNesting == 0) {
mHistoryCur.states2 |= HistoryItem.STATE2_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
- mVideoOn = true;
mVideoOnTimer.startRunningLocked(elapsedRealtime);
}
+ mVideoOnNesting++;
getUidStatsLocked(uid).noteVideoTurnedOnLocked(elapsedRealtime);
}
public void noteVideoOffLocked(int uid) {
+ if (mVideoOnNesting == 0) {
+ return;
+ }
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
- if (mVideoOn) {
+ if (--mVideoOnNesting == 0) {
mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
- mVideoOn = false;
mVideoOnTimer.stopRunningLocked(elapsedRealtime);
}
getUidStatsLocked(uid).noteVideoTurnedOffLocked(elapsedRealtime);
}
+ public void noteResetAudioLocked() {
+ if (mAudioOnNesting > 0) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ mAudioOnNesting = 0;
+ mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mAudioOnTimer.stopAllRunningLocked(elapsedRealtime);
+ for (int i=0; i<mUidStats.size(); i++) {
+ BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+ uid.noteResetAudioLocked(elapsedRealtime);
+ }
+ }
+ }
+
+ public void noteResetVideoLocked() {
+ if (mVideoOnNesting > 0) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ mAudioOnNesting = 0;
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mVideoOnTimer.stopAllRunningLocked(elapsedRealtime);
+ for (int i=0; i<mUidStats.size(); i++) {
+ BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+ uid.noteResetVideoLocked(elapsedRealtime);
+ }
+ }
+ }
+
public void noteActivityResumedLocked(int uid) {
uid = mapUid(uid);
getUidStatsLocked(uid).noteActivityResumedLocked(SystemClock.elapsedRealtime());
@@ -3855,10 +3902,7 @@
boolean mWifiMulticastEnabled;
StopwatchTimer mWifiMulticastTimer;
- boolean mAudioTurnedOn;
StopwatchTimer mAudioTurnedOnTimer;
-
- boolean mVideoTurnedOn;
StopwatchTimer mVideoTurnedOnTimer;
StopwatchTimer mForegroundActivityTimer;
@@ -4073,52 +4117,48 @@
public StopwatchTimer createAudioTurnedOnTimerLocked() {
if (mAudioTurnedOnTimer == null) {
mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
- null, mOnBatteryTimeBase);
+ mAudioTurnedOnTimers, mOnBatteryTimeBase);
}
return mAudioTurnedOnTimer;
}
- @Override
public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
- if (!mAudioTurnedOn) {
- mAudioTurnedOn = true;
- createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ }
+
+ public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
+ if (mAudioTurnedOnTimer != null) {
+ mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
- @Override
- public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
- if (mAudioTurnedOn) {
- mAudioTurnedOn = false;
- if (mAudioTurnedOnTimer != null) {
- mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
- }
+ public void noteResetAudioLocked(long elapsedRealtimeMs) {
+ if (mAudioTurnedOnTimer != null) {
+ mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
}
}
public StopwatchTimer createVideoTurnedOnTimerLocked() {
if (mVideoTurnedOnTimer == null) {
mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
- null, mOnBatteryTimeBase);
+ mVideoTurnedOnTimers, mOnBatteryTimeBase);
}
return mVideoTurnedOnTimer;
}
- @Override
public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
- if (!mVideoTurnedOn) {
- mVideoTurnedOn = true;
- createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ }
+
+ public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
+ if (mVideoTurnedOnTimer != null) {
+ mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
- @Override
- public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
- if (mVideoTurnedOn) {
- mVideoTurnedOn = false;
- if (mVideoTurnedOnTimer != null) {
- mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
- }
+ public void noteResetVideoLocked(long elapsedRealtimeMs) {
+ if (mVideoTurnedOnTimer != null) {
+ mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
}
}
@@ -4416,11 +4456,9 @@
}
if (mAudioTurnedOnTimer != null) {
active |= !mAudioTurnedOnTimer.reset(false);
- active |= mAudioTurnedOn;
}
if (mVideoTurnedOnTimer != null) {
active |= !mVideoTurnedOnTimer.reset(false);
- active |= mVideoTurnedOn;
}
if (mForegroundActivityTimer != null) {
active |= !mForegroundActivityTimer.reset(false);
@@ -4803,17 +4841,15 @@
} else {
mWifiMulticastTimer = null;
}
- mAudioTurnedOn = false;
if (in.readInt() != 0) {
mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
- null, mOnBatteryTimeBase, in);
+ mAudioTurnedOnTimers, mOnBatteryTimeBase, in);
} else {
mAudioTurnedOnTimer = null;
}
- mVideoTurnedOn = false;
if (in.readInt() != 0) {
mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
- null, mOnBatteryTimeBase, in);
+ mVideoTurnedOnTimers, mOnBatteryTimeBase, in);
} else {
mVideoTurnedOnTimer = null;
}
@@ -7554,11 +7590,9 @@
if (in.readInt() != 0) {
u.mWifiMulticastTimer.readSummaryFromParcelLocked(in);
}
- u.mAudioTurnedOn = false;
if (in.readInt() != 0) {
u.createAudioTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
}
- u.mVideoTurnedOn = false;
if (in.readInt() != 0) {
u.createVideoTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
}
@@ -8071,9 +8105,9 @@
mBluetoothStateTimer[i] = new StopwatchTimer(null, -500-i,
null, mOnBatteryTimeBase, in);
}
- mAudioOn = false;
+ mAudioOnNesting = 0;
mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
- mVideoOn = false;
+ mVideoOnNesting = 0;
mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
mFlashlightOn = false;
mFlashlightOnTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in);
@@ -8125,6 +8159,8 @@
mWifiScanTimers.clear();
mWifiBatchedScanTimers.clear();
mWifiMulticastTimers.clear();
+ mAudioTurnedOnTimers.clear();
+ mVideoTurnedOnTimers.clear();
sNumSpeedSteps = in.readInt();
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index cc481e1..7e8d828 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -18,6 +18,8 @@
import android.Manifest;
import android.app.ActivityManagerNative;
+import android.app.AlarmClockInfo;
+import android.app.AlarmManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.appwidget.AppWidgetManager;
@@ -254,15 +256,11 @@
getCurrentOrCallingUserId());
}
- /**
- * Returns the actual password mode, as set by keyguard after updating the password.
- *
- * @return
- */
public void reportFailedPasswordAttempt() {
- getDevicePolicyManager().reportFailedPasswordAttempt(getCurrentOrCallingUserId());
- getTrustManager().reportUnlockAttempt(false /* authenticated */,
- getCurrentOrCallingUserId());
+ int userId = getCurrentOrCallingUserId();
+ getDevicePolicyManager().reportFailedPasswordAttempt(userId);
+ getTrustManager().reportUnlockAttempt(false /* authenticated */, userId);
+ getTrustManager().reportRequireCredentialEntry(userId);
}
public void reportSuccessfulPasswordAttempt() {
@@ -357,7 +355,8 @@
* @return Whether the password matches any in the history.
*/
public boolean checkPasswordHistory(String password) {
- String passwordHashString = new String(passwordToHash(password));
+ String passwordHashString = new String(
+ passwordToHash(password, getCurrentOrCallingUserId()));
String passwordHistory = getString(PASSWORD_HISTORY_KEY);
if (passwordHistory == null) {
return false;
@@ -826,7 +825,7 @@
if (passwordHistoryLength == 0) {
passwordHistory = "";
} else {
- byte[] hash = passwordToHash(password);
+ byte[] hash = passwordToHash(password, userHandle);
passwordHistory = new String(hash) + "," + passwordHistory;
// Cut it to contain passwordHistoryLength hashes
// and passwordHistoryLength -1 commas.
@@ -942,13 +941,13 @@
}
}
- private String getSalt() {
- long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0);
+ private String getSalt(int userId) {
+ long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0, userId);
if (salt == 0) {
try {
salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
- setLong(LOCK_PASSWORD_SALT_KEY, salt);
- Log.v(TAG, "Initialized lock password salt");
+ setLong(LOCK_PASSWORD_SALT_KEY, salt, userId);
+ Log.v(TAG, "Initialized lock password salt for user: " + userId);
} catch (NoSuchAlgorithmException e) {
// Throw an exception rather than storing a password we'll never be able to recover
throw new IllegalStateException("Couldn't get SecureRandom number", e);
@@ -964,14 +963,14 @@
* @param password the gesture pattern.
* @return the hash of the pattern in a byte array.
*/
- public byte[] passwordToHash(String password) {
+ public byte[] passwordToHash(String password, int userId) {
if (password == null) {
return null;
}
String algo = null;
byte[] hashed = null;
try {
- byte[] saltedPassword = (password + getSalt()).getBytes();
+ byte[] saltedPassword = (password + getSalt(userId)).getBytes();
byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword);
hashed = (toHex(sha1) + toHex(md5)).getBytes();
@@ -1170,13 +1169,9 @@
* @return A formatted string of the next alarm (for showing on the lock screen),
* or null if there is no next alarm.
*/
- public String getNextAlarm() {
- String nextAlarm = Settings.System.getStringForUser(mContentResolver,
- Settings.System.NEXT_ALARM_FORMATTED, UserHandle.USER_CURRENT);
- if (nextAlarm == null || TextUtils.isEmpty(nextAlarm)) {
- return null;
- }
- return nextAlarm;
+ public AlarmClockInfo getNextAlarm() {
+ AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ return alarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
}
private boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) {
@@ -1338,6 +1333,14 @@
return true;
}
+ private long getLong(String secureSettingKey, long defaultValue, int userHandle) {
+ try {
+ return getLockSettings().getLong(secureSettingKey, defaultValue, userHandle);
+ } catch (RemoteException re) {
+ return defaultValue;
+ }
+ }
+
private long getLong(String secureSettingKey, long defaultValue) {
try {
return getLockSettings().getLong(secureSettingKey, defaultValue,
diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
index e1a4909..5da5ae9 100644
--- a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
+++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
@@ -27,6 +27,8 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -154,6 +156,11 @@
mToolbar.setSubtitleTextAppearance(mToolbar.getContext(), subtitleTextStyle);
}
+ final int popupTheme = a.getResourceId(R.styleable.ActionBar_popupTheme, 0);
+ if (popupTheme != 0) {
+ mToolbar.setPopupTheme(popupTheme);
+ }
+
a.recycle();
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 0e22174..480383b 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -23,7 +23,6 @@
LOCAL_SRC_FILES:= \
AndroidRuntime.cpp \
- Time.cpp \
com_android_internal_content_NativeLibraryHelper.cpp \
com_google_android_gles_jni_EGLImpl.cpp \
com_google_android_gles_jni_GLImpl.cpp.arm \
@@ -81,7 +80,6 @@
android_net_NetUtils.cpp \
android_net_TrafficStats.cpp \
android_nio_utils.cpp \
- android_text_format_Time.cpp \
android_util_AssetManager.cpp \
android_util_Binder.cpp \
android_util_EventLog.cpp \
@@ -90,12 +88,12 @@
android_util_Process.cpp \
android_util_StringBlock.cpp \
android_util_XmlBlock.cpp \
+ android_graphics_Canvas.cpp \
android_graphics_Picture.cpp \
android/graphics/AutoDecodeCancel.cpp \
android/graphics/Bitmap.cpp \
android/graphics/BitmapFactory.cpp \
android/graphics/Camera.cpp \
- android/graphics/Canvas.cpp \
android/graphics/CanvasProperty.cpp \
android/graphics/ColorFilter.cpp \
android/graphics/DrawFilter.cpp \
@@ -113,6 +111,7 @@
android/graphics/NinePatchImpl.cpp \
android/graphics/NinePatchPeeker.cpp \
android/graphics/Paint.cpp \
+ android/graphics/PaintImpl.cpp \
android/graphics/Path.cpp \
android/graphics/PathMeasure.cpp \
android/graphics/PathEffect.cpp \
@@ -122,6 +121,7 @@
android/graphics/Rasterizer.cpp \
android/graphics/Region.cpp \
android/graphics/Shader.cpp \
+ android/graphics/SkiaCanvas.cpp \
android/graphics/SurfaceTexture.cpp \
android/graphics/Typeface.cpp \
android/graphics/TypefaceImpl.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 92a8fca..3a02ab9 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -144,7 +144,6 @@
extern int register_android_database_SQLiteGlobal(JNIEnv* env);
extern int register_android_database_SQLiteDebug(JNIEnv* env);
extern int register_android_nio_utils(JNIEnv* env);
-extern int register_android_text_format_Time(JNIEnv* env);
extern int register_android_os_Debug(JNIEnv* env);
extern int register_android_os_MessageQueue(JNIEnv* env);
extern int register_android_os_Parcel(JNIEnv* env);
@@ -1223,7 +1222,6 @@
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
REG_JNI(register_android_util_FloatMath),
- REG_JNI(register_android_text_format_Time),
REG_JNI(register_android_content_AssetManager),
REG_JNI(register_android_content_StringBlock),
REG_JNI(register_android_content_XmlBlock),
diff --git a/core/jni/Time.cpp b/core/jni/Time.cpp
deleted file mode 100644
index f3037f3..0000000
--- a/core/jni/Time.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-#include "TimeUtils.h"
-#include <stdio.h>
-#include <cutils/tztime.h>
-
-namespace android {
-
-static void
-dump(const Time& t)
-{
- #ifdef HAVE_TM_GMTOFF
- long tm_gmtoff = t.t.tm_gmtoff;
- #else
- long tm_gmtoff = 0;
- #endif
- printf("%04d-%02d-%02d %02d:%02d:%02d (%d,%ld,%d,%d)\n",
- t.t.tm_year+1900, t.t.tm_mon+1, t.t.tm_mday,
- t.t.tm_hour, t.t.tm_min, t.t.tm_sec,
- t.t.tm_isdst, tm_gmtoff, t.t.tm_wday, t.t.tm_yday);
-}
-
-Time::Time()
-{
- t.tm_sec = 0;
- t.tm_min = 0;
- t.tm_hour = 0;
- t.tm_mday = 0;
- t.tm_mon = 0;
- t.tm_year = 0;
- t.tm_wday = 0;
- t.tm_yday = 0;
- t.tm_isdst = -1; // we don't know, so let the C library determine
- #ifdef HAVE_TM_GMTOFF
- t.tm_gmtoff = 0;
- #endif
-}
-
-
-#define COMPARE_FIELD(field) do { \
- int diff = a.t.field - b.t.field; \
- if (diff != 0) return diff; \
- } while(0)
-
-int
-Time::compare(Time& a, Time& b)
-{
- if (0 == strcmp(a.timezone, b.timezone)) {
- // if the timezones are the same, we can easily compare the two
- // times. Otherwise, convert to milliseconds and compare that.
- // This requires that object be normalized.
- COMPARE_FIELD(tm_year);
- COMPARE_FIELD(tm_mon);
- COMPARE_FIELD(tm_mday);
- COMPARE_FIELD(tm_hour);
- COMPARE_FIELD(tm_min);
- COMPARE_FIELD(tm_sec);
- return 0;
- } else {
- int64_t am = a.toMillis(false /* use isDst */);
- int64_t bm = b.toMillis(false /* use isDst */);
- int64_t diff = am-bm;
- return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0);
- }
-}
-
-static const int DAYS_PER_MONTH[] = {
- 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
- };
-
-static inline int days_this_month(int year, int month)
-{
- int n = DAYS_PER_MONTH[month];
- if (n != 28) {
- return n;
- } else {
- int y = year;
- return ((y%4)==0&&((y%100)!=0||(y%400)==0)) ? 29 : 28;
- }
-}
-
-void
-Time::switchTimezone(const char* timezone)
-{
- time_t seconds = mktime_tz(&(this->t), this->timezone);
- localtime_tz(&seconds, &(this->t), timezone);
-}
-
-String8
-Time::format(const char *format, const struct strftime_locale *locale) const
-{
- char buf[257];
- int n = strftime_tz(buf, 257, format, &(this->t), locale);
- if (n > 0) {
- return String8(buf);
- } else {
- return String8();
- }
-}
-
-static inline short
-tochar(int n)
-{
- return (n >= 0 && n <= 9) ? ('0'+n) : ' ';
-}
-
-static inline short
-next_char(int *m, int k)
-{
- int n = *m / k;
- *m = *m % k;
- return tochar(n);
-}
-
-void
-Time::format2445(short* buf, bool hasTime) const
-{
- int n;
-
- n = t.tm_year+1900;
- buf[0] = next_char(&n, 1000);
- buf[1] = next_char(&n, 100);
- buf[2] = next_char(&n, 10);
- buf[3] = tochar(n);
-
- n = t.tm_mon+1;
- buf[4] = next_char(&n, 10);
- buf[5] = tochar(n);
-
- n = t.tm_mday;
- buf[6] = next_char(&n, 10);
- buf[7] = tochar(n);
-
- if (hasTime) {
- buf[8] = 'T';
-
- n = t.tm_hour;
- buf[9] = next_char(&n, 10);
- buf[10] = tochar(n);
-
- n = t.tm_min;
- buf[11] = next_char(&n, 10);
- buf[12] = tochar(n);
-
- n = t.tm_sec;
- buf[13] = next_char(&n, 10);
- buf[14] = tochar(n);
- bool inUtc = strcmp("UTC", timezone) == 0;
- if (inUtc) {
- buf[15] = 'Z';
- }
- }
-}
-
-String8
-Time::toString() const
-{
- String8 str;
- char* s = str.lockBuffer(150);
- #ifdef HAVE_TM_GMTOFF
- long tm_gmtoff = t.tm_gmtoff;
- #else
- long tm_gmtoff = 0;
- #endif
- sprintf(s, "%04d%02d%02dT%02d%02d%02d%s(%d,%d,%ld,%d,%d)",
- t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min,
- t.tm_sec, timezone, t.tm_wday, t.tm_yday, tm_gmtoff, t.tm_isdst,
- (int)(((Time*)this)->toMillis(false /* use isDst */)/1000));
- str.unlockBuffer();
- return str;
-}
-
-void
-Time::setToNow()
-{
- time_t seconds;
- time(&seconds);
- localtime_tz(&seconds, &(this->t), this->timezone);
-}
-
-int64_t
-Time::toMillis(bool ignoreDst)
-{
- if (ignoreDst) {
- this->t.tm_isdst = -1;
- }
- int64_t r = mktime_tz(&(this->t), this->timezone);
- if (r == -1)
- return -1;
- return r * 1000;
-}
-
-void
-Time::set(int64_t millis)
-{
- time_t seconds = millis / 1000;
- localtime_tz(&seconds, &(this->t), this->timezone);
-}
-
-}; // namespace android
-
diff --git a/core/jni/TimeUtils.h b/core/jni/TimeUtils.h
deleted file mode 100644
index b19e021..0000000
--- a/core/jni/TimeUtils.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#ifndef ANDROID_TIME_H
-#define ANDROID_TIME_H
-
-#include <time.h>
-#include <cutils/tztime.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-
-namespace android {
-
-/*
- * This class is the core implementation of the android.util.Time java
- * class. It doesn't implement some of the methods that are implemented
- * in Java. They could be done here, but it's not expected that this class
- * will be used. If that assumption is incorrect, feel free to update this
- * file. The reason to do it here is to not mix the implementation of this
- * class and the jni glue code.
- */
-class Time
-{
-public:
- struct tm t;
-
- // this object doesn't own this string
- const char *timezone;
-
- enum {
- SEC = 1,
- MIN = 2,
- HOUR = 3,
- MDAY = 4,
- MON = 5,
- YEAR = 6,
- WDAY = 7,
- YDAY = 8
- };
-
- static int compare(Time& a, Time& b);
-
- Time();
-
- void switchTimezone(const char *timezone);
- String8 format(const char *format, const struct strftime_locale *locale) const;
- void format2445(short* buf, bool hasTime) const;
- String8 toString() const;
- void setToNow();
- int64_t toMillis(bool ignoreDst);
- void set(int64_t millis);
-
- inline void set(int sec, int min, int hour, int mday, int mon, int year,
- int isdst)
- {
- this->t.tm_sec = sec;
- this->t.tm_min = min;
- this->t.tm_hour = hour;
- this->t.tm_mday = mday;
- this->t.tm_mon = mon;
- this->t.tm_year = year;
- this->t.tm_isdst = isdst;
-#ifdef HAVE_TM_GMTOFF
- this->t.tm_gmtoff = 0;
-#endif
- this->t.tm_wday = 0;
- this->t.tm_yday = 0;
- }
-};
-
-}; // namespace android
-
-#endif // ANDROID_TIME_H
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
old mode 100644
new mode 100755
index 53aca3d..70cf9a8
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1,3 +1,4 @@
+#include "Paint.h"
#include "SkBitmap.h"
#include "SkPixelRef.h"
#include "SkImageEncoder.h"
@@ -676,7 +677,7 @@
jlong srcHandle, jlong paintHandle,
jintArray offsetXY) {
const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
- const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ const android::Paint* paint = reinterpret_cast<android::Paint*>(paintHandle);
SkIPoint offset;
SkBitmap* dst = new SkBitmap;
JavaPixelAllocator allocator(env);
@@ -845,7 +846,19 @@
const int h = bm0->height();
const size_t size = bm0->width() * bm0->bytesPerPixel();
for (int y = 0; y < h; y++) {
- if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
+ // SkBitmap::getAddr(int, int) may return NULL due to unrecognized config
+ // (ex: kRLE_Index8_Config). This will cause memcmp method to crash. Since bm0
+ // and bm1 both have pixel data() (have passed NULL == getPixels() check),
+ // those 2 bitmaps should be valid (only unrecognized), we return JNI_FALSE
+ // to warn user those 2 unrecognized config bitmaps may be different.
+ void *bm0Addr = bm0->getAddr(0, y);
+ void *bm1Addr = bm1->getAddr(0, y);
+
+ if(bm0Addr == NULL || bm1Addr == NULL) {
+ return JNI_FALSE;
+ }
+
+ if (memcmp(bm0Addr, bm1Addr, size) != 0) {
return JNI_FALSE;
}
}
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index d17f46c..9f832b0 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -3,6 +3,7 @@
#include "SkCamera.h"
+#include "Canvas.h"
#include "GraphicsJNI.h"
static jfieldID gNativeInstanceFieldID;
@@ -95,10 +96,10 @@
}
static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) {
- SkCanvas* native_canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
+ SkCanvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
- v->applyToCanvas((SkCanvas*)native_canvas);
+ v->applyToCanvas(canvas);
}
static jfloat Camera_dotWithNormal(JNIEnv* env, jobject obj,
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
deleted file mode 100644
index 6254f3d..0000000
--- a/core/jni/android/graphics/Canvas.cpp
+++ /dev/null
@@ -1,1330 +0,0 @@
-/*
- * Copyright (C) 2006-2007 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.
- */
-
-#include "jni.h"
-#include "GraphicsJNI.h"
-#include <android_runtime/AndroidRuntime.h>
-
-#include "SkCanvas.h"
-#include "SkClipStack.h"
-#include "SkDevice.h"
-#include "SkDeque.h"
-#include "SkDrawFilter.h"
-#include "SkGraphics.h"
-#include <SkImageInfo.h>
-#include "SkPorterDuff.h"
-#include "SkShader.h"
-#include "SkTArray.h"
-#include "SkTemplates.h"
-
-#include <minikin/Layout.h>
-#include "MinikinSkia.h"
-#include "MinikinUtils.h"
-
-#include "TypefaceImpl.h"
-
-#include "unicode/ubidi.h"
-#include "unicode/ushape.h"
-
-#include <utils/Log.h>
-
-namespace android {
-
-class ClipCopier : public SkCanvas::ClipVisitor {
-public:
- ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
-
- virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) {
- m_dstCanvas->clipRect(rect, op, antialias);
- }
- virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) {
- m_dstCanvas->clipRRect(rrect, op, antialias);
- }
- virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) {
- m_dstCanvas->clipPath(path, op, antialias);
- }
-
-private:
- SkCanvas* m_dstCanvas;
-};
-
-// Holds an SkCanvas reference plus additional native data.
-class NativeCanvasWrapper {
-private:
- struct SaveRec {
- int saveCount;
- SkCanvas::SaveFlags saveFlags;
- };
-
-public:
- NativeCanvasWrapper(SkCanvas* canvas)
- : mCanvas(canvas)
- , mSaveStack(NULL) {
- SkASSERT(canvas);
- }
-
- ~NativeCanvasWrapper() {
- delete mSaveStack;
- }
-
- SkCanvas* getCanvas() const {
- return mCanvas.get();
- }
-
- void setCanvas(SkCanvas* canvas) {
- SkASSERT(canvas);
- mCanvas.reset(canvas);
-
- delete mSaveStack;
- mSaveStack = NULL;
- }
-
- int save(SkCanvas::SaveFlags flags) {
- int count = mCanvas->save();
- recordPartialSave(flags);
- return count;
- }
-
- int saveLayer(const SkRect* bounds, const SkPaint* paint,
- SkCanvas::SaveFlags flags) {
- int count = mCanvas->saveLayer(bounds, paint,
- static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
- recordPartialSave(flags);
- return count;
- }
-
- int saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
- SkCanvas::SaveFlags flags) {
- int count = mCanvas->saveLayerAlpha(bounds, alpha,
- static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
- recordPartialSave(flags);
- return count;
- }
-
- void restore() {
- const SaveRec* rec = (NULL == mSaveStack)
- ? NULL
- : static_cast<SaveRec*>(mSaveStack->back());
- int currentSaveCount = mCanvas->getSaveCount() - 1;
- SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
-
- if (NULL == rec || rec->saveCount != currentSaveCount) {
- // Fast path - no record for this frame.
- mCanvas->restore();
- return;
- }
-
- bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
- bool preserveClip = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
-
- SkMatrix savedMatrix;
- if (preserveMatrix) {
- savedMatrix = mCanvas->getTotalMatrix();
- }
-
- SkTArray<SkClipStack::Element> savedClips;
- if (preserveClip) {
- saveClipsForFrame(savedClips, currentSaveCount);
- }
-
- mCanvas->restore();
-
- if (preserveMatrix) {
- mCanvas->setMatrix(savedMatrix);
- }
-
- if (preserveClip && !savedClips.empty()) {
- applyClips(savedClips);
- }
-
- mSaveStack->pop_back();
- }
-
-private:
- void recordPartialSave(SkCanvas::SaveFlags flags) {
- // A partial save is a save operation which doesn't capture the full canvas state.
- // (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
-
- // Mask-out non canvas state bits.
- flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
-
- if (SkCanvas::kMatrixClip_SaveFlag == flags) {
- // not a partial save.
- return;
- }
-
- if (NULL == mSaveStack) {
- mSaveStack = new SkDeque(sizeof(struct SaveRec), 8);
- }
-
- SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
- // Store the save counter in the SkClipStack domain.
- // (0-based, equal to the number of save ops on the stack).
- rec->saveCount = mCanvas->getSaveCount() - 1;
- rec->saveFlags = flags;
- }
-
- void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips,
- int frameSaveCount) {
- SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
- SkClipStack::Iter::kTop_IterStart);
- while (const SkClipStack::Element* elem = clipIterator.next()) {
- if (elem->getSaveCount() < frameSaveCount) {
- // done with the current frame.
- break;
- }
- SkASSERT(elem->getSaveCount() == frameSaveCount);
- clips.push_back(*elem);
- }
- }
-
- void applyClips(const SkTArray<SkClipStack::Element>& clips) {
- ClipCopier clipCopier(mCanvas);
-
- // The clip stack stores clips in device space.
- SkMatrix origMatrix = mCanvas->getTotalMatrix();
- mCanvas->resetMatrix();
-
- // We pushed the clips in reverse order.
- for (int i = clips.count() - 1; i >= 0; --i) {
- clips[i].replay(&clipCopier);
- }
-
- mCanvas->setMatrix(origMatrix);
- }
-
- SkAutoTUnref<SkCanvas> mCanvas;
- SkDeque* mSaveStack; // lazily allocated, tracks partial saves.
-};
-
-// Returns true if the SkCanvas's clip is non-empty.
-static jboolean hasNonEmptyClip(const SkCanvas& canvas) {
- bool emptyClip = canvas.isClipEmpty();
- return emptyClip ? JNI_FALSE : JNI_TRUE;
-}
-
-class SkCanvasGlue {
-public:
- // Get the native wrapper for a given handle.
- static inline NativeCanvasWrapper* getNativeWrapper(jlong nativeHandle) {
- SkASSERT(nativeHandle);
- return reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
- }
-
- // Get the SkCanvas for a given native handle.
- static inline SkCanvas* getNativeCanvas(jlong nativeHandle) {
- NativeCanvasWrapper* wrapper = getNativeWrapper(nativeHandle);
- SkCanvas* canvas = wrapper->getCanvas();
- SkASSERT(canvas);
-
- return canvas;
- }
-
- // Construct an SkCanvas from the bitmap.
- static SkCanvas* createCanvas(SkBitmap* bitmap) {
- if (bitmap) {
- return SkNEW_ARGS(SkCanvas, (*bitmap));
- }
-
- // Create an empty bitmap device to prevent callers from crashing
- // if they attempt to draw into this canvas.
- SkBitmap emptyBitmap;
- return new SkCanvas(emptyBitmap);
- }
-
- // Copy the canvas matrix & clip state.
- static void copyCanvasState(SkCanvas* srcCanvas, SkCanvas* dstCanvas) {
- if (srcCanvas && dstCanvas) {
- dstCanvas->setMatrix(srcCanvas->getTotalMatrix());
- if (NULL != srcCanvas->getDevice() && NULL != dstCanvas->getDevice()) {
- ClipCopier copier(dstCanvas);
- srcCanvas->replayClips(&copier);
- }
- }
- }
-
- // Native JNI handlers
- static void finalizer(JNIEnv* env, jobject clazz, jlong nativeHandle) {
- NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
- delete wrapper;
- }
-
- // Native wrapper constructor used by Canvas(Bitmap)
- static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
- // No check - 0 is a valid bitmapHandle.
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkCanvas* canvas = createCanvas(bitmap);
-
- return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
- }
-
- // Native wrapper constructor used by Canvas(native_canvas)
- static jlong initCanvas(JNIEnv* env, jobject, jlong canvasHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
- return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
- }
-
- // Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
- // optionally copying canvas matrix & clip state.
- static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
- jboolean copyState) {
- NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(canvasHandle);
- SkCanvas* newCanvas = createCanvas(reinterpret_cast<SkBitmap*>(bitmapHandle));
- NPE_CHECK_RETURN_VOID(env, newCanvas);
-
- if (copyState == JNI_TRUE) {
- copyCanvasState(wrapper->getCanvas(), newCanvas);
- }
-
- // setCanvas() unrefs the old canvas.
- wrapper->setCanvas(newCanvas);
- }
-
- static void freeCaches(JNIEnv* env, jobject) {
- SkGraphics::PurgeFontCache();
- }
-
- static void freeTextLayoutCaches(JNIEnv* env, jobject) {
- Layout::purgeCaches();
- }
-
- static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- bool result = canvas->getDevice()->accessBitmap(false).isOpaque();
- return result ? JNI_TRUE : JNI_FALSE;
- }
-
- static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- int width = canvas->getDevice()->accessBitmap(false).width();
- return static_cast<jint>(width);
- }
-
- static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- int height = canvas->getDevice()->accessBitmap(false).height();
- return static_cast<jint>(height);
- }
-
- static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
- NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
- SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
- return static_cast<jint>(wrapper->save(flags));
- }
-
- static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle,
- jfloat l, jfloat t, jfloat r, jfloat b,
- jlong paintHandle, jint flagsHandle) {
- NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
- SkRect bounds;
- bounds.set(l, t, r, b);
- return static_cast<jint>(wrapper->saveLayer(&bounds, paint, flags));
- }
-
- static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle,
- jfloat l, jfloat t, jfloat r, jfloat b,
- jint alpha, jint flagsHandle) {
- NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
- SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
- SkRect bounds;
- bounds.set(l, t, r, b);
- return static_cast<jint>(wrapper->saveLayerAlpha(&bounds, alpha, flags));
- }
-
- static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
- NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
- if (wrapper->getCanvas()->getSaveCount() <= 1) { // cannot restore anymore
- doThrowISE(env, "Underflow in restore");
- return;
- }
- wrapper->restore();
- }
-
- static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
- return static_cast<jint>(getNativeCanvas(canvasHandle)->getSaveCount());
- }
-
- static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle,
- jint restoreCount) {
- NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
- if (restoreCount < 1) {
- doThrowIAE(env, "Underflow in restoreToCount");
- return;
- }
-
- while (wrapper->getCanvas()->getSaveCount() > restoreCount) {
- wrapper->restore();
- }
- }
-
- static void translate(JNIEnv*, jobject, jlong canvasHandle,
- jfloat dx, jfloat dy) {
- getNativeCanvas(canvasHandle)->translate(dx, dy);
- }
-
- static void scale__FF(JNIEnv*, jobject, jlong canvasHandle,
- jfloat sx, jfloat sy) {
- getNativeCanvas(canvasHandle)->scale(sx, sy);
- }
-
- static void rotate__F(JNIEnv*, jobject, jlong canvasHandle,
- jfloat degrees) {
- getNativeCanvas(canvasHandle)->rotate(degrees);
- }
-
- static void skew__FF(JNIEnv*, jobject, jlong canvasHandle,
- jfloat sx, jfloat sy) {
- getNativeCanvas(canvasHandle)->skew(sx, sy);
- }
-
- static void concat(JNIEnv* env, jobject, jlong canvasHandle,
- jlong matrixHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
- canvas->concat(*matrix);
- }
-
- static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle,
- jlong matrixHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
- if (NULL == matrix) {
- canvas->resetMatrix();
- } else {
- canvas->setMatrix(*matrix);
- }
- }
-
- static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle,
- jfloat left, jfloat top, jfloat right,
- jfloat bottom, jint op) {
- SkRect r;
- r.set(left, top, right, bottom);
- SkCanvas* c = getNativeCanvas(canvasHandle);
- c->clipRect(r, static_cast<SkRegion::Op>(op));
- return hasNonEmptyClip(*c);
- }
-
- static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle,
- jlong pathHandle, jint op) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- canvas->clipPath(*reinterpret_cast<SkPath*>(pathHandle),
- static_cast<SkRegion::Op>(op));
- return hasNonEmptyClip(*canvas);
- }
-
- static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle,
- jlong deviceRgnHandle, jint op) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
- SkPath rgnPath;
- if (deviceRgn->getBoundaryPath(&rgnPath)) {
- // The region is specified in device space.
- SkMatrix savedMatrix = canvas->getTotalMatrix();
- canvas->resetMatrix();
- canvas->clipPath(rgnPath, static_cast<SkRegion::Op>(op));
- canvas->setMatrix(savedMatrix);
- } else {
- canvas->clipRect(SkRect::MakeEmpty(), static_cast<SkRegion::Op>(op));
- }
- return hasNonEmptyClip(*canvas);
- }
-
- static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle,
- jlong filterHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- canvas->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
- }
-
- static jboolean quickReject__Path(JNIEnv* env, jobject, jlong canvasHandle,
- jlong pathHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- bool result = canvas->quickReject(*reinterpret_cast<SkPath*>(pathHandle));
- return result ? JNI_TRUE : JNI_FALSE;
- }
-
- static jboolean quickReject__FFFF(JNIEnv* env, jobject, jlong canvasHandle,
- jfloat left, jfloat top, jfloat right,
- jfloat bottom) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkRect r;
- r.set(left, top, right, bottom);
- bool result = canvas->quickReject(r);
- return result ? JNI_TRUE : JNI_FALSE;
- }
-
- static void drawRGB(JNIEnv* env, jobject, jlong canvasHandle,
- jint r, jint g, jint b) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- canvas->drawARGB(0xFF, r, g, b);
- }
-
- static void drawARGB(JNIEnv* env, jobject, jlong canvasHandle,
- jint a, jint r, jint g, jint b) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- canvas->drawARGB(a, r, g, b);
- }
-
- static void drawColor__I(JNIEnv* env, jobject, jlong canvasHandle,
- jint color) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- canvas->drawColor(color);
- }
-
- static void drawColor__II(JNIEnv* env, jobject, jlong canvasHandle,
- jint color, jint modeHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle);
- canvas->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
- }
-
- static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle,
- jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- canvas->drawPaint(*paint);
- }
-
- static void doPoints(JNIEnv* env, jlong canvasHandle,
- jfloatArray jptsArray, jint offset, jint count,
- jlong paintHandle, jint modeHandle) {
- NPE_CHECK_RETURN_VOID(env, jptsArray);
- SkCanvas::PointMode mode = static_cast<SkCanvas::PointMode>(modeHandle);
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
- AutoJavaFloatArray autoPts(env, jptsArray);
- float* floats = autoPts.ptr();
- const int length = autoPts.length();
-
- if ((offset | count) < 0 || offset + count > length) {
- doThrowAIOOBE(env);
- return;
- }
-
- // now convert the floats into SkPoints
- count >>= 1; // now it is the number of points
- SkAutoSTMalloc<32, SkPoint> storage(count);
- SkPoint* pts = storage.get();
- const float* src = floats + offset;
- for (int i = 0; i < count; i++) {
- pts[i].set(src[0], src[1]);
- src += 2;
- }
- canvas->drawPoints(mode, count, pts, *paint);
- }
-
- static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle,
- jfloatArray jptsArray, jint offset,
- jint count, jlong paintHandle) {
- doPoints(env, canvasHandle, jptsArray, offset, count, paintHandle,
- SkCanvas::kPoints_PointMode);
- }
-
- static void drawLines(JNIEnv* env, jobject, jlong canvasHandle,
- jfloatArray jptsArray, jint offset, jint count,
- jlong paintHandle) {
- doPoints(env, canvasHandle, jptsArray, offset, count, paintHandle,
- SkCanvas::kLines_PointMode);
- }
-
- static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
- jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- canvas->drawPoint(x, y, *paint);
- }
-
- static void drawLine__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
- jfloat startX, jfloat startY, jfloat stopX,
- jfloat stopY, jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- canvas->drawLine(startX, startY, stopX, stopY, *paint);
- }
-
- static void drawRect__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
- jfloat left, jfloat top, jfloat right,
- jfloat bottom, jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- canvas->drawRectCoords(left, top, right, bottom, *paint);
- }
-
- static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
- jfloat right, jfloat bottom, jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
- canvas->drawOval(oval, *paint);
- }
-
- static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx,
- jfloat cy, jfloat radius, jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- canvas->drawCircle(cx, cy, radius, *paint);
- }
-
- static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
- jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle, jboolean useCenter,
- jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
- canvas->drawArc(oval, startAngle, sweepAngle, useCenter, *paint);
- }
-
- static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle,
- jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat rx, jfloat ry,
- jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
- canvas->drawRoundRect(rect, rx, ry, *paint);
- }
-
- static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
- jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- canvas->drawPath(*path, *paint);
- }
-
- static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject jcanvas,
- jlong canvasHandle, jlong bitmapHandle,
- jfloat left, jfloat top,
- jlong paintHandle, jint canvasDensity,
- jint screenDensity, jint bitmapDensity) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
- if (canvasDensity == bitmapDensity || canvasDensity == 0
- || bitmapDensity == 0) {
- if (screenDensity != 0 && screenDensity != bitmapDensity) {
- SkPaint filteredPaint;
- if (paint) {
- filteredPaint = *paint;
- }
- filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
- canvas->drawBitmap(*bitmap, left, top, &filteredPaint);
- } else {
- canvas->drawBitmap(*bitmap, left, top, paint);
- }
- } else {
- canvas->save();
- SkScalar scale = canvasDensity / (float)bitmapDensity;
- canvas->translate(left, top);
- canvas->scale(scale, scale);
-
- SkPaint filteredPaint;
- if (paint) {
- filteredPaint = *paint;
- }
- filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
-
- canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
-
- canvas->restore();
- }
- }
-
- static void doDrawBitmap(JNIEnv* env, SkCanvas* canvas, SkBitmap* bitmap,
- jobject srcIRect, const SkRect& dst, SkPaint* paint,
- jint screenDensity, jint bitmapDensity) {
- SkIRect src, *srcPtr = NULL;
-
- if (NULL != srcIRect) {
- GraphicsJNI::jrect_to_irect(env, srcIRect, &src);
- srcPtr = &src;
- }
-
- if (screenDensity != 0 && screenDensity != bitmapDensity) {
- SkPaint filteredPaint;
- if (paint) {
- filteredPaint = *paint;
- }
- filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
- canvas->drawBitmapRect(*bitmap, srcPtr, dst, &filteredPaint);
- } else {
- canvas->drawBitmapRect(*bitmap, srcPtr, dst, paint);
- }
- }
-
- static void drawBitmapRF(JNIEnv* env, jobject, jlong canvasHandle,
- jlong bitmapHandle, jobject srcIRect,
- jobject dstRectF, jlong paintHandle,
- jint screenDensity, jint bitmapDensity) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkRect dst;
- GraphicsJNI::jrectf_to_rect(env, dstRectF, &dst);
- doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
- screenDensity, bitmapDensity);
- }
-
- static void drawBitmapRR(JNIEnv* env, jobject, jlong canvasHandle,
- jlong bitmapHandle, jobject srcIRect,
- jobject dstRect, jlong paintHandle,
- jint screenDensity, jint bitmapDensity) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkRect dst;
- GraphicsJNI::jrect_to_rect(env, dstRect, &dst);
- doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
- screenDensity, bitmapDensity);
- }
-
- static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
- jintArray jcolors, jint offset, jint stride,
- jfloat x, jfloat y, jint width, jint height,
- jboolean hasAlpha, jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
- // correct the alphaType to kOpaque_SkAlphaType.
- SkImageInfo info = SkImageInfo::Make(width, height,
- hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
- kPremul_SkAlphaType);
- SkBitmap bitmap;
- if (!bitmap.allocPixels(info)) {
- return;
- }
-
- if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride,
- 0, 0, width, height, bitmap)) {
- return;
- }
-
- canvas->drawBitmap(bitmap, x, y, paint);
- }
-
- static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle,
- jlong bitmapHandle, jlong matrixHandle,
- jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
- const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- canvas->drawBitmapMatrix(*bitmap, *matrix, paint);
- }
-
- static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle,
- jlong bitmapHandle, jint meshWidth, jint meshHeight,
- jfloatArray jverts, jint vertIndex, jintArray jcolors,
- jint colorIndex, jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
- const int ptCount = (meshWidth + 1) * (meshHeight + 1);
- const int indexCount = meshWidth * meshHeight * 6;
-
- AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
- AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
-
- /* Our temp storage holds 2 or 3 arrays.
- texture points [ptCount * sizeof(SkPoint)]
- optionally vertex points [ptCount * sizeof(SkPoint)] if we need a
- copy to convert from float to fixed
- indices [ptCount * sizeof(uint16_t)]
- */
- ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[]
- storageSize += indexCount * sizeof(uint16_t); // indices[]
-
- SkAutoMalloc storage(storageSize);
- SkPoint* texs = (SkPoint*)storage.get();
- SkPoint* verts;
- uint16_t* indices;
-#ifdef SK_SCALAR_IS_FLOAT
- verts = (SkPoint*)(vertA.ptr() + vertIndex);
- indices = (uint16_t*)(texs + ptCount);
-#else
- SkASSERT(false);
-#endif
-
- // cons up texture coordinates and indices
- {
- const SkScalar w = SkIntToScalar(bitmap->width());
- const SkScalar h = SkIntToScalar(bitmap->height());
- const SkScalar dx = w / meshWidth;
- const SkScalar dy = h / meshHeight;
-
- SkPoint* texsPtr = texs;
- SkScalar y = 0;
- for (int i = 0; i <= meshHeight; i++) {
- if (i == meshHeight) {
- y = h; // to ensure numerically we hit h exactly
- }
- SkScalar x = 0;
- for (int j = 0; j < meshWidth; j++) {
- texsPtr->set(x, y);
- texsPtr += 1;
- x += dx;
- }
- texsPtr->set(w, y);
- texsPtr += 1;
- y += dy;
- }
- SkASSERT(texsPtr - texs == ptCount);
- }
-
- // cons up indices
- {
- uint16_t* indexPtr = indices;
- int index = 0;
- for (int i = 0; i < meshHeight; i++) {
- for (int j = 0; j < meshWidth; j++) {
- // lower-left triangle
- *indexPtr++ = index;
- *indexPtr++ = index + meshWidth + 1;
- *indexPtr++ = index + meshWidth + 2;
- // upper-right triangle
- *indexPtr++ = index;
- *indexPtr++ = index + meshWidth + 2;
- *indexPtr++ = index + 1;
- // bump to the next cell
- index += 1;
- }
- // bump to the next row
- index += 1;
- }
- SkASSERT(indexPtr - indices == indexCount);
- SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize);
- }
-
- // double-check that we have legal indices
-#ifdef SK_DEBUG
- {
- for (int i = 0; i < indexCount; i++) {
- SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
- }
- }
-#endif
-
- // cons-up a shader for the bitmap
- SkPaint tmpPaint;
- if (paint) {
- tmpPaint = *paint;
- }
- SkShader* shader = SkShader::CreateBitmapShader(*bitmap,
- SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
- SkSafeUnref(tmpPaint.setShader(shader));
-
- canvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, verts,
- texs, (const SkColor*)colorA.ptr(), NULL, indices,
- indexCount, tmpPaint);
- }
-
- static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
- jint modeHandle, jint vertexCount,
- jfloatArray jverts, jint vertIndex,
- jfloatArray jtexs, jint texIndex,
- jintArray jcolors, jint colorIndex,
- jshortArray jindices, jint indexIndex,
- jint indexCount, jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
- const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
- AutoJavaFloatArray vertA(env, jverts, vertIndex + vertexCount);
- AutoJavaFloatArray texA(env, jtexs, texIndex + vertexCount);
- AutoJavaIntArray colorA(env, jcolors, colorIndex + vertexCount);
- AutoJavaShortArray indexA(env, jindices, indexIndex + indexCount);
-
- const int ptCount = vertexCount >> 1;
-
- SkPoint* verts;
- SkPoint* texs = NULL;
-#ifdef SK_SCALAR_IS_FLOAT
- verts = (SkPoint*)(vertA.ptr() + vertIndex);
- if (jtexs != NULL) {
- texs = (SkPoint*)(texA.ptr() + texIndex);
- }
-#else
- SkASSERT(false);
-#endif
-
- const SkColor* colors = NULL;
- const uint16_t* indices = NULL;
- if (jcolors != NULL) {
- colors = (const SkColor*)(colorA.ptr() + colorIndex);
- }
- if (jindices != NULL) {
- indices = (const uint16_t*)(indexA.ptr() + indexIndex);
- }
-
- canvas->drawVertices(mode, ptCount, verts, texs, colors, NULL,
- indices, indexCount, *paint);
- }
-
-
- static void drawText___CIIFFIPaintTypeface(JNIEnv* env, jobject, jlong canvasHandle,
- jcharArray text, jint index, jint count,
- jfloat x, jfloat y, jint bidiFlags,
- jlong paintHandle, jlong typefaceHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
- jchar* textArray = env->GetCharArrayElements(text, NULL);
- drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, bidiFlags, paint, typeface);
- env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
- }
-
- static void drawText__StringIIFFIPaintTypeface(JNIEnv* env, jobject,
- jlong canvasHandle, jstring text,
- jint start, jint end,
- jfloat x, jfloat y, jint bidiFlags,
- jlong paintHandle, jlong typefaceHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
- const jchar* textArray = env->GetStringChars(text, NULL);
- drawTextWithGlyphs(canvas, textArray, start, end, x, y, bidiFlags, paint, typeface);
- env->ReleaseStringChars(text, textArray);
- }
-
- class DrawTextFunctor {
- public:
- DrawTextFunctor(const Layout& layout, SkCanvas* canvas, jfloat x, jfloat y, SkPaint* paint,
- uint16_t* glyphs, SkPoint* pos)
- : layout(layout), canvas(canvas), x(x), y(y), paint(paint), glyphs(glyphs),
- pos(pos) { }
-
- void operator()(size_t start, size_t end) {
- for (size_t i = start; i < end; i++) {
- glyphs[i] = layout.getGlyphId(i);
- pos[i].fX = x + layout.getX(i);
- pos[i].fY = y + layout.getY(i);
- }
- canvas->drawPosText(glyphs + start, (end - start) << 1, pos + start, *paint);
- }
- private:
- const Layout& layout;
- SkCanvas* canvas;
- jfloat x;
- jfloat y;
- SkPaint* paint;
- uint16_t* glyphs;
- SkPoint* pos;
- };
-
- static void drawGlyphsToSkia(SkCanvas* canvas, SkPaint* paint, const Layout& layout, float x, float y) {
- size_t nGlyphs = layout.nGlyphs();
- uint16_t* glyphs = new uint16_t[nGlyphs];
- SkPoint* pos = new SkPoint[nGlyphs];
-
- x += MinikinUtils::xOffsetForTextAlign(paint, layout);
- SkPaint::Align align = paint->getTextAlign();
- paint->setTextAlign(SkPaint::kLeft_Align);
- paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- DrawTextFunctor f(layout, canvas, x, y, paint, glyphs, pos);
- MinikinUtils::forFontRun(layout, paint, f);
- doDrawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
- paint->setTextAlign(align);
- delete[] glyphs;
- delete[] pos;
- }
-
- static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
- int start, int end,
- jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
-
- jint count = end - start;
- drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, bidiFlags, paint,
- typeface);
- }
-
- static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
- int start, int count, int contextCount,
- jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
-
- Layout layout;
- std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
- layout.doLayout(textArray, start, count, contextCount, css);
- drawGlyphsToSkia(canvas, paint, layout, x, y);
- }
-
-// Same values used by Skia
-#define kStdStrikeThru_Offset (-6.0f / 21.0f)
-#define kStdUnderline_Offset (1.0f / 9.0f)
-#define kStdUnderline_Thickness (1.0f / 18.0f)
-
- static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat length,
- SkPaint* paint) {
- uint32_t flags;
- SkDrawFilter* drawFilter = canvas->getDrawFilter();
- if (drawFilter) {
- SkPaint paintCopy(*paint);
- drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
- flags = paintCopy.getFlags();
- } else {
- flags = paint->getFlags();
- }
- if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
- SkScalar left = x;
- SkScalar right = x + length;
- float textSize = paint->getTextSize();
- float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
- if (flags & SkPaint::kUnderlineText_Flag) {
- SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
- SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
- canvas->drawRectCoords(left, top, right, bottom, *paint);
- }
- if (flags & SkPaint::kStrikeThruText_Flag) {
- SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
- SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
- canvas->drawRectCoords(left, top, right, bottom, *paint);
- }
- }
- }
-
- static void doDrawGlyphsPos(SkCanvas* canvas, const jchar* glyphArray, const jfloat* posArray,
- int index, int count, jfloat x, jfloat y, SkPaint* paint) {
- SkPoint* posPtr = new SkPoint[count];
- for (int indx = 0; indx < count; indx++) {
- posPtr[indx].fX = x + posArray[indx * 2];
- posPtr[indx].fY = y + posArray[indx * 2 + 1];
- }
- canvas->drawPosText(glyphArray, count << 1, posPtr, *paint);
- delete[] posPtr;
- }
-
- static void drawTextRun___CIIIIFFZPaintTypeface(
- JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
- jint count, jint contextIndex, jint contextCount,
- jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
- int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
- jchar* chars = env->GetCharArrayElements(text, NULL);
- drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex,
- count, contextCount, x, y, bidiFlags, paint, typeface);
- env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
- }
-
- static void drawTextRun__StringIIIIFFZPaintTypeface(
- JNIEnv* env, jobject obj, jlong canvasHandle, jstring text, jint start,
- jint end, jint contextStart, jint contextEnd,
- jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
- int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
- jint count = end - start;
- jint contextCount = contextEnd - contextStart;
- const jchar* chars = env->GetStringChars(text, NULL);
- drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart,
- count, contextCount, x, y, bidiFlags, paint, typeface);
- env->ReleaseStringChars(text, chars);
- }
-
- static void drawPosText___CII_FPaint(JNIEnv* env, jobject, jlong canvasHandle,
- jcharArray text, jint index, jint count,
- jfloatArray pos, jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- jchar* textArray = text ? env->GetCharArrayElements(text, NULL) : NULL;
- jsize textCount = text ? env->GetArrayLength(text) : NULL;
- float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
- int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
- SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
- int indx;
- for (indx = 0; indx < posCount; indx++) {
- posPtr[indx].fX = posArray[indx << 1];
- posPtr[indx].fY = posArray[(indx << 1) + 1];
- }
-
- SkPaint::TextEncoding encoding = paint->getTextEncoding();
- paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
- canvas->drawPosText(textArray + index, count << 1, posPtr, *paint);
- paint->setTextEncoding(encoding);
-
- if (text) {
- env->ReleaseCharArrayElements(text, textArray, 0);
- }
- if (pos) {
- env->ReleaseFloatArrayElements(pos, posArray, 0);
- }
- delete[] posPtr;
- }
-
- static void drawPosText__String_FPaint(JNIEnv* env, jobject,
- jlong canvasHandle, jstring text,
- jfloatArray pos,
- jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- const void* text_ = text ? env->GetStringChars(text, NULL) : NULL;
- int byteLength = text ? env->GetStringLength(text) : 0;
- float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
- int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
- SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
-
- for (int indx = 0; indx < posCount; indx++) {
- posPtr[indx].fX = posArray[indx << 1];
- posPtr[indx].fY = posArray[(indx << 1) + 1];
- }
-
- SkPaint::TextEncoding encoding = paint->getTextEncoding();
- paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
- canvas->drawPosText(text_, byteLength << 1, posPtr, *paint);
- paint->setTextEncoding(encoding);
-
- if (text) {
- env->ReleaseStringChars(text, (const jchar*) text_);
- }
- if (pos) {
- env->ReleaseFloatArrayElements(pos, posArray, 0);
- }
- delete[] posPtr;
- }
-
- class DrawTextOnPathFunctor {
- public:
- DrawTextOnPathFunctor(const Layout& layout, SkCanvas* canvas, float hOffset,
- float vOffset, SkPaint* paint, SkPath* path)
- : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
- paint(paint), path(path) {
- }
- void operator()(size_t start, size_t end) {
- uint16_t glyphs[1];
- for (size_t i = start; i < end; i++) {
- glyphs[0] = layout.getGlyphId(i);
- float x = hOffset + layout.getX(i);
- float y = vOffset + layout.getY(i);
- canvas->drawTextOnPathHV(glyphs, sizeof(glyphs), *path, x, y, *paint);
- }
- }
- private:
- const Layout& layout;
- SkCanvas* canvas;
- float hOffset;
- float vOffset;
- SkPaint* paint;
- SkPath* path;
- };
-
- static void doDrawTextOnPath(SkPaint* paint, const jchar* text, int count, int bidiFlags,
- float hOffset, float vOffset, SkPath* path, SkCanvas* canvas, TypefaceImpl* typeface) {
- Layout layout;
- std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
- layout.doLayout(text, 0, count, count, css);
- hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path);
- // Set align to left for drawing, as we don't want individual
- // glyphs centered or right-aligned; the offset above takes
- // care of all alignment.
- SkPaint::Align align = paint->getTextAlign();
- paint->setTextAlign(SkPaint::kLeft_Align);
-
- DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paint, path);
- MinikinUtils::forFontRun(layout, paint, f);
- paint->setTextAlign(align);
- }
-
- static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
- jlong canvasHandle, jcharArray text, jint index, jint count,
- jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
- jlong typefaceHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
- jchar* textArray = env->GetCharArrayElements(text, NULL);
- doDrawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
- path, canvas, typeface);
- env->ReleaseCharArrayElements(text, textArray, 0);
- }
-
- static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
- jlong canvasHandle, jstring text, jlong pathHandle,
- jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
- jlong typefaceHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
- const jchar* text_ = env->GetStringChars(text, NULL);
- int count = env->GetStringLength(text);
- doDrawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
- path, canvas, typeface);
- env->ReleaseStringChars(text, text_);
- }
-
-
- // This function is a mirror of SkCanvas::getClipBounds except that it does
- // not outset the edge of the clip to account for anti-aliasing. There is
- // a skia bug to investigate pushing this logic into back into skia.
- // (see https://code.google.com/p/skia/issues/detail?id=1303)
- static bool getHardClipBounds(SkCanvas* canvas, SkRect* bounds) {
- SkIRect ibounds;
- if (!canvas->getClipDeviceBounds(&ibounds)) {
- return false;
- }
-
- SkMatrix inverse;
- // if we can't invert the CTM, we can't return local clip bounds
- if (!canvas->getTotalMatrix().invert(&inverse)) {
- if (bounds) {
- bounds->setEmpty();
- }
- return false;
- }
-
- if (NULL != bounds) {
- SkRect r = SkRect::Make(ibounds);
- inverse.mapRect(bounds, r);
- }
- return true;
- }
-
- static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle,
- jobject bounds) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkRect r;
- SkIRect ir;
- bool result = getHardClipBounds(canvas, &r);
-
- if (!result) {
- r.setEmpty();
- }
- r.round(&ir);
-
- (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
- return result ? JNI_TRUE : JNI_FALSE;
- }
-
- static void getCTM(JNIEnv* env, jobject, jlong canvasHandle,
- jlong matrixHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
- *matrix = canvas->getTotalMatrix();
- }
-};
-
-static JNINativeMethod gCanvasMethods[] = {
- {"finalizer", "(J)V", (void*) SkCanvasGlue::finalizer},
- {"initRaster", "(J)J", (void*) SkCanvasGlue::initRaster},
- {"initCanvas", "(J)J", (void*) SkCanvasGlue::initCanvas},
- {"native_setBitmap", "(JJZ)V", (void*) SkCanvasGlue::setBitmap},
- {"native_isOpaque","(J)Z", (void*) SkCanvasGlue::isOpaque},
- {"native_getWidth","(J)I", (void*) SkCanvasGlue::getWidth},
- {"native_getHeight","(J)I", (void*) SkCanvasGlue::getHeight},
- {"native_save","(JI)I", (void*) SkCanvasGlue::save},
- {"native_saveLayer","(JFFFFJI)I", (void*) SkCanvasGlue::saveLayer},
- {"native_saveLayerAlpha","(JFFFFII)I", (void*) SkCanvasGlue::saveLayerAlpha},
- {"native_restore","(J)V", (void*) SkCanvasGlue::restore},
- {"native_getSaveCount","(J)I", (void*) SkCanvasGlue::getSaveCount},
- {"native_restoreToCount","(JI)V", (void*) SkCanvasGlue::restoreToCount},
- {"native_translate","(JFF)V", (void*) SkCanvasGlue::translate},
- {"native_scale","(JFF)V", (void*) SkCanvasGlue::scale__FF},
- {"native_rotate","(JF)V", (void*) SkCanvasGlue::rotate__F},
- {"native_skew","(JFF)V", (void*) SkCanvasGlue::skew__FF},
- {"native_concat","(JJ)V", (void*) SkCanvasGlue::concat},
- {"native_setMatrix","(JJ)V", (void*) SkCanvasGlue::setMatrix},
- {"native_clipRect","(JFFFFI)Z", (void*) SkCanvasGlue::clipRect},
- {"native_clipPath","(JJI)Z", (void*) SkCanvasGlue::clipPath},
- {"native_clipRegion","(JJI)Z", (void*) SkCanvasGlue::clipRegion},
- {"nativeSetDrawFilter", "(JJ)V", (void*) SkCanvasGlue::setDrawFilter},
- {"native_getClipBounds","(JLandroid/graphics/Rect;)Z",
- (void*) SkCanvasGlue::getClipBounds},
- {"native_getCTM", "(JJ)V", (void*)SkCanvasGlue::getCTM},
- {"native_quickReject","(JJ)Z", (void*) SkCanvasGlue::quickReject__Path},
- {"native_quickReject","(JFFFF)Z", (void*)SkCanvasGlue::quickReject__FFFF},
- {"native_drawRGB","(JIII)V", (void*) SkCanvasGlue::drawRGB},
- {"native_drawARGB","(JIIII)V", (void*) SkCanvasGlue::drawARGB},
- {"native_drawColor","(JI)V", (void*) SkCanvasGlue::drawColor__I},
- {"native_drawColor","(JII)V", (void*) SkCanvasGlue::drawColor__II},
- {"native_drawPaint","(JJ)V", (void*) SkCanvasGlue::drawPaint},
- {"native_drawPoint", "(JFFJ)V", (void*) SkCanvasGlue::drawPoint},
- {"native_drawPoints", "(J[FIIJ)V", (void*) SkCanvasGlue::drawPoints},
- {"native_drawLines", "(J[FIIJ)V", (void*) SkCanvasGlue::drawLines},
- {"native_drawLine","(JFFFFJ)V", (void*) SkCanvasGlue::drawLine__FFFFPaint},
- {"native_drawRect","(JFFFFJ)V", (void*) SkCanvasGlue::drawRect__FFFFPaint},
- {"native_drawOval","(JFFFFJ)V", (void*) SkCanvasGlue::drawOval},
- {"native_drawCircle","(JFFFJ)V", (void*) SkCanvasGlue::drawCircle},
- {"native_drawArc","(JFFFFFFZJ)V", (void*) SkCanvasGlue::drawArc},
- {"native_drawRoundRect","(JFFFFFFJ)V",
- (void*) SkCanvasGlue::drawRoundRect},
- {"native_drawPath","(JJJ)V", (void*) SkCanvasGlue::drawPath},
- {"native_drawBitmap","(JJFFJIII)V",
- (void*) SkCanvasGlue::drawBitmap__BitmapFFPaint},
- {"native_drawBitmap","(JJLandroid/graphics/Rect;Landroid/graphics/RectF;JII)V",
- (void*) SkCanvasGlue::drawBitmapRF},
- {"native_drawBitmap","(JJLandroid/graphics/Rect;Landroid/graphics/Rect;JII)V",
- (void*) SkCanvasGlue::drawBitmapRR},
- {"native_drawBitmap", "(J[IIIFFIIZJ)V",
- (void*)SkCanvasGlue::drawBitmapArray},
- {"nativeDrawBitmapMatrix", "(JJJJ)V",
- (void*)SkCanvasGlue::drawBitmapMatrix},
- {"nativeDrawBitmapMesh", "(JJII[FI[IIJ)V",
- (void*)SkCanvasGlue::drawBitmapMesh},
- {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V",
- (void*)SkCanvasGlue::drawVertices},
- {"native_drawText","(J[CIIFFIJJ)V",
- (void*) SkCanvasGlue::drawText___CIIFFIPaintTypeface},
- {"native_drawText","(JLjava/lang/String;IIFFIJJ)V",
- (void*) SkCanvasGlue::drawText__StringIIFFIPaintTypeface},
- {"native_drawTextRun","(J[CIIIIFFZJJ)V",
- (void*) SkCanvasGlue::drawTextRun___CIIIIFFZPaintTypeface},
- {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V",
- (void*) SkCanvasGlue::drawTextRun__StringIIIIFFZPaintTypeface},
- {"native_drawTextOnPath","(J[CIIJFFIJJ)V",
- (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint},
- {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V",
- (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint},
-
- {"freeCaches", "()V", (void*) SkCanvasGlue::freeCaches},
-
- {"freeTextLayoutCaches", "()V", (void*) SkCanvasGlue::freeTextLayoutCaches}
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include <android_runtime/AndroidRuntime.h>
-
-#define REG(env, name, array) \
- result = android::AndroidRuntime::registerNativeMethods(env, name, array, \
- SK_ARRAY_COUNT(array)); \
- if (result < 0) return result
-
-int register_android_graphics_Canvas(JNIEnv* env) {
- int result;
-
- REG(env, "android/graphics/Canvas", gCanvasMethods);
-
- return result;
-}
-
-} // namespace android
-
-// GraphicsJNI helper for external clients.
-// We keep the implementation here to avoid exposing NativeCanvasWrapper
-// externally.
-SkCanvas* GraphicsJNI::getNativeCanvas(jlong nativeHandle) {
- return android::SkCanvasGlue::getNativeCanvas(nativeHandle);
-}
diff --git a/core/jni/android/graphics/Canvas.h b/core/jni/android/graphics/Canvas.h
new file mode 100644
index 0000000..3cd57f4
--- /dev/null
+++ b/core/jni/android/graphics/Canvas.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GRAPHICS_CANVAS_H
+#define ANDROID_GRAPHICS_CANVAS_H
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkMatrix.h"
+
+namespace android {
+
+class Canvas {
+public:
+ virtual ~Canvas() {};
+
+ static Canvas* create_canvas(SkBitmap* bitmap);
+ static Canvas* create_canvas(SkCanvas* skiaCanvas);
+
+ // TODO: enable HWUI to either create similar canvas wrapper or subclass
+ // directly from Canvas
+ //static Canvas* create_canvas(uirenderer::Renderer* renderer);
+
+ // TODO: this is a temporary affordance until all necessary logic can be
+ // moved within this interface! Further, the return value should
+ // NOT be unref'd and is valid until this canvas is destroyed or a
+ // new bitmap is set.
+ virtual SkCanvas* getSkCanvas() = 0;
+
+ virtual void setBitmap(SkBitmap* bitmap, bool copyState) = 0;
+
+ virtual bool isOpaque() = 0;
+ virtual int width() = 0;
+ virtual int height() = 0;
+
+// ----------------------------------------------------------------------------
+// Canvas state operations
+// ----------------------------------------------------------------------------
+ // Save (layer)
+ virtual int getSaveCount() const = 0;
+ virtual int save(SkCanvas::SaveFlags flags) = 0;
+ virtual void restore() = 0;
+ virtual void restoreToCount(int saveCount) = 0;
+
+ virtual int saveLayer(float left, float top, float right, float bottom,
+ const SkPaint* paint, SkCanvas::SaveFlags flags) = 0;
+ virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+ int alpha, SkCanvas::SaveFlags flags) = 0;
+
+ // Matrix
+ virtual void getMatrix(SkMatrix* outMatrix) const = 0;
+ virtual void setMatrix(const SkMatrix& matrix) = 0;
+
+ virtual void concat(const SkMatrix& matrix) = 0;
+ virtual void rotate(float degrees) = 0;
+ virtual void scale(float sx, float sy) = 0;
+ virtual void skew(float sx, float sy) = 0;
+ virtual void translate(float dx, float dy) = 0;
+
+ // clip
+ virtual bool getClipBounds(SkRect* outRect) const = 0;
+ virtual bool quickRejectRect(float left, float top, float right, float bottom) const = 0;
+ virtual bool quickRejectPath(const SkPath& path) const = 0;
+
+ virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) = 0;
+ virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0;
+ virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
+
+ // filters
+ virtual SkDrawFilter* getDrawFilter() = 0;
+ virtual void setDrawFilter(SkDrawFilter* drawFilter) = 0;
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations
+// ----------------------------------------------------------------------------
+ virtual void drawColor(int color, SkXfermode::Mode mode) = 0;
+ virtual void drawPaint(const SkPaint& paint) = 0;
+
+ // Geometry
+ virtual void drawPoint(float x, float y, const SkPaint& paint) = 0;
+ virtual void drawPoints(const float* points, int count, const SkPaint& paint) = 0;
+ virtual void drawLine(float startX, float startY, float stopX, float stopY,
+ const SkPaint& paint) = 0;
+ virtual void drawLines(const float* points, int count, const SkPaint& paint) = 0;
+ virtual void drawRect(float left, float top, float right, float bottom,
+ const SkPaint& paint) = 0;
+ virtual void drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint& paint) = 0;
+ virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0;
+ virtual void drawOval(float left, float top, float right, float bottom,
+ const SkPaint& paint) = 0;
+ virtual void drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) = 0;
+ virtual void drawPath(const SkPath& path, const SkPaint& paint) = 0;
+ virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+ const float* verts, const float* tex, const int* colors,
+ const uint16_t* indices, int indexCount, const SkPaint& paint) = 0;
+
+ // Bitmap-based
+ virtual void drawBitmap(const SkBitmap& bitmap, float left, float top,
+ const SkPaint* paint) = 0;
+ virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) = 0;
+ virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) = 0;
+ virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) = 0;
+
+ // Text
+ virtual void drawText(const uint16_t* text, const float* positions, int count,
+ const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom) = 0;
+ virtual void drawPosText(const uint16_t* text, const float* positions, int count,
+ int posCount, const SkPaint& paint) = 0;
+ virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) = 0;
+
+ /*
+ * Specifies if the positions passed to ::drawText are absolute or relative
+ * to the (x,y) value provided.
+ *
+ * If true the (x,y) values are ignored. Otherwise, those (x,y) values need
+ * to be added to each glyph's position to get its absolute position.
+ */
+ virtual bool drawTextAbsolutePos() const = 0;
+};
+
+}; // namespace android
+#endif // ANDROID_GRAPHICS_CANVAS_H
diff --git a/core/jni/android/graphics/CanvasProperty.cpp b/core/jni/android/graphics/CanvasProperty.cpp
index cfa9cd8..e63c5fa 100644
--- a/core/jni/android/graphics/CanvasProperty.cpp
+++ b/core/jni/android/graphics/CanvasProperty.cpp
@@ -16,6 +16,7 @@
#include "jni.h"
#include "GraphicsJNI.h"
+#include "Paint.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/RefBase.h>
@@ -32,7 +33,7 @@
}
static jlong createPaint(JNIEnv* env, jobject clazz, jlong paintPtr) {
- const SkPaint* paint = reinterpret_cast<const SkPaint*>(paintPtr);
+ const Paint* paint = reinterpret_cast<const Paint*>(paintPtr);
return reinterpret_cast<jlong>(new CanvasPropertyPaint(*paint));
}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 5cc2b95..7c41c2e 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -4,6 +4,7 @@
#include "JNIHelp.h"
#include "GraphicsJNI.h"
+#include "Canvas.h"
#include "SkCanvas.h"
#include "SkDevice.h"
#include "SkMath.h"
@@ -364,17 +365,17 @@
SkASSERT(canvas);
SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID);
- SkCanvas* c = getNativeCanvas(canvasHandle);
+ SkCanvas* c = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
SkASSERT(c);
return c;
}
-SkPaint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) {
+android::Paint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) {
SkASSERT(env);
SkASSERT(paint);
SkASSERT(env->IsInstanceOf(paint, gPaint_class));
jlong paintHandle = env->GetLongField(paint, gPaint_nativeInstanceID);
- SkPaint* p = reinterpret_cast<SkPaint*>(paintHandle);
+ android::Paint* p = reinterpret_cast<android::Paint*>(paintHandle);
SkASSERT(p);
return p;
}
@@ -385,7 +386,6 @@
SkASSERT(env->IsInstanceOf(paint, gPaint_class));
jlong typefaceHandle = env->GetLongField(paint, gPaint_nativeTypefaceID);
android::TypefaceImpl* p = reinterpret_cast<android::TypefaceImpl*>(typefaceHandle);
- SkASSERT(p);
return p;
}
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 8150edf..dcc97e5 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -12,9 +12,9 @@
class SkBitmapRegionDecoder;
class SkCanvas;
-class SkPaint;
namespace android {
+class Paint;
class TypefaceImpl;
}
@@ -47,9 +47,8 @@
static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point);
static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf);
- static SkCanvas* getNativeCanvas(jlong nativeHandle);
static SkCanvas* getNativeCanvas(JNIEnv*, jobject canvas);
- static SkPaint* getNativePaint(JNIEnv*, jobject paint);
+ static android::Paint* getNativePaint(JNIEnv*, jobject paint);
static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint);
static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap);
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp
index f02f118..802f2abe 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/core/jni/android/graphics/MinikinUtils.cpp
@@ -18,9 +18,9 @@
#include <cutils/log.h>
#include <string>
-#include "SkPaint.h"
#include "SkPathMeasure.h"
#include "minikin/Layout.h"
+#include "Paint.h"
#include "TypefaceImpl.h"
#include "MinikinSkia.h"
@@ -29,6 +29,8 @@
namespace android {
// Do an sprintf starting at offset n, abort on overflow
+static int snprintfcat(char* buf, int off, int size, const char* format, ...)
+ __attribute__((__format__(__printf__, 4, 5)));
static int snprintfcat(char* buf, int off, int size, const char* format, ...) {
va_list args;
va_start(args, format);
@@ -38,22 +40,23 @@
return off + n;
}
-std::string MinikinUtils::setLayoutProperties(Layout* layout, const SkPaint* paint, int bidiFlags,
+std::string MinikinUtils::setLayoutProperties(Layout* layout, const Paint* paint, int bidiFlags,
TypefaceImpl* typeface) {
TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(typeface);
layout->setFontCollection(resolvedFace->fFontCollection);
FontStyle style = resolvedFace->fStyle;
- char css[256];
+ char css[512];
int off = snprintfcat(css, 0, sizeof(css),
"font-size: %d; font-scale-x: %f; font-skew-x: %f; -paint-flags: %d;"
- " font-weight: %d; font-style: %s; -minikin-bidi: %d;",
+ " font-weight: %d; font-style: %s; -minikin-bidi: %d; letter-spacing: %f;",
(int)paint->getTextSize(),
paint->getTextScaleX(),
paint->getTextSkewX(),
MinikinFontSkia::packPaintFlags(paint),
style.getWeight() * 100,
style.getItalic() ? "italic" : "normal",
- bidiFlags);
+ bidiFlags,
+ paint->getLetterSpacing());
SkString langString = paint->getPaintOptionsAndroid().getLanguage().getTag();
off = snprintfcat(css, off, sizeof(css), " lang: %s;", langString.c_str());
SkPaintOptionsAndroid::FontVariant var = paint->getPaintOptionsAndroid().getFontVariant();
@@ -62,12 +65,12 @@
return std::string(css);
}
-float MinikinUtils::xOffsetForTextAlign(SkPaint* paint, const Layout& layout) {
+float MinikinUtils::xOffsetForTextAlign(Paint* paint, const Layout& layout) {
switch (paint->getTextAlign()) {
- case SkPaint::kCenter_Align:
+ case Paint::kCenter_Align:
return layout.getAdvance() * -0.5f;
break;
- case SkPaint::kRight_Align:
+ case Paint::kRight_Align:
return -layout.getAdvance();
break;
default:
@@ -76,13 +79,13 @@
return 0;
}
-float MinikinUtils::hOffsetForTextAlign(SkPaint* paint, const Layout& layout, const SkPath& path) {
+float MinikinUtils::hOffsetForTextAlign(Paint* paint, const Layout& layout, const SkPath& path) {
float align = 0;
switch (paint->getTextAlign()) {
- case SkPaint::kCenter_Align:
+ case Paint::kCenter_Align:
align = -0.5f;
break;
- case SkPaint::kRight_Align:
+ case Paint::kRight_Align:
align = -1;
break;
default:
diff --git a/core/jni/android/graphics/MinikinUtils.h b/core/jni/android/graphics/MinikinUtils.h
index 1663860..0562c3b1 100644
--- a/core/jni/android/graphics/MinikinUtils.h
+++ b/core/jni/android/graphics/MinikinUtils.h
@@ -16,7 +16,7 @@
/**
* Utilities for making Minikin work, especially from existing objects like
- * SkPaint and so on.
+ * Paint and so on.
**/
// TODO: does this really need to be separate from MinikinSkia?
@@ -24,6 +24,8 @@
#ifndef ANDROID_MINIKIN_UTILS_H
#define ANDROID_MINIKIN_UTILS_H
+#include "Paint.h"
+
namespace android {
// TODO: these should be defined in Minikin's Layout.h
@@ -43,15 +45,15 @@
class MinikinUtils {
public:
- static std::string setLayoutProperties(Layout* layout, const SkPaint* paint, int bidiFlags,
+ static std::string setLayoutProperties(Layout* layout, const Paint* paint, int bidiFlags,
TypefaceImpl* typeface);
- static float xOffsetForTextAlign(SkPaint* paint, const Layout& layout);
+ static float xOffsetForTextAlign(Paint* paint, const Layout& layout);
- static float hOffsetForTextAlign(SkPaint* paint, const Layout& layout, const SkPath& path);
+ static float hOffsetForTextAlign(Paint* paint, const Layout& layout, const SkPath& path);
// f is a functor of type void f(size_t start, size_t end);
template <typename F>
- static void forFontRun(const Layout& layout, SkPaint* paint, F& f) {
+ static void forFontRun(const Layout& layout, Paint* paint, F& f) {
float saveSkewX = paint->getTextSkewX();
bool savefakeBold = paint->isFakeBoldText();
MinikinFont* curFont = NULL;
diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp
index 5f9a9b7..226f83e 100644
--- a/core/jni/android/graphics/Movie.cpp
+++ b/core/jni/android/graphics/Movie.cpp
@@ -7,6 +7,7 @@
#include "SkUtils.h"
#include "Utils.h"
#include "CreateJavaOutputStreamAdaptor.h"
+#include "Paint.h"
#include <androidfw/Asset.h>
#include <androidfw/ResourceTypes.h>
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index ab5bdb0..cf23771 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -23,6 +23,8 @@
#include <Caches.h>
+#include "Paint.h"
+#include "Canvas.h"
#include "SkCanvas.h"
#include "SkRegion.h"
#include "GraphicsJNI.h"
@@ -119,10 +121,10 @@
static void drawF(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRectF,
jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
jint destDensity, jint srcDensity) {
- SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
+ SkCanvas* canvas = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle);
- const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
SkASSERT(canvas);
SkASSERT(boundsRectF);
SkASSERT(bitmap);
@@ -138,10 +140,10 @@
static void drawI(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRect,
jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
jint destDensity, jint srcDensity) {
- SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
+ SkCanvas* canvas = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle);
- const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
SkASSERT(canvas);
SkASSERT(boundsRect);
SkASSERT(bitmap);
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 0ad390a..e2b3684 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -39,6 +39,7 @@
#include <minikin/Layout.h>
#include "MinikinSkia.h"
#include "MinikinUtils.h"
+#include "Paint.h"
#include "TypefaceImpl.h"
// temporary for debugging
@@ -61,47 +62,47 @@
static jclass gFontMetricsInt_class;
static JMetricsID gFontMetricsInt_fieldID;
-static void defaultSettingsForAndroid(SkPaint* paint) {
+static void defaultSettingsForAndroid(Paint* paint) {
// GlyphID encoding is required because we are using Harfbuzz shaping
- paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
SkPaintOptionsAndroid paintOpts = paint->getPaintOptionsAndroid();
paintOpts.setUseFontFallbacks(true);
paint->setPaintOptionsAndroid(paintOpts);
}
-class SkPaintGlue {
+class PaintGlue {
public:
enum MoveOpt {
AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT
};
static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
delete obj;
}
static jlong init(JNIEnv* env, jobject clazz) {
- SkPaint* obj = new SkPaint();
+ Paint* obj = new Paint();
defaultSettingsForAndroid(obj);
return reinterpret_cast<jlong>(obj);
}
static jlong initWithPaint(JNIEnv* env, jobject clazz, jlong paintHandle) {
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkPaint* obj = new SkPaint(*paint);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ Paint* obj = new Paint(*paint);
return reinterpret_cast<jlong>(obj);
}
static void reset(JNIEnv* env, jobject clazz, jlong objHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
obj->reset();
defaultSettingsForAndroid(obj);
}
static void assign(JNIEnv* env, jobject clazz, jlong dstPaintHandle, jlong srcPaintHandle) {
- SkPaint* dst = reinterpret_cast<SkPaint*>(dstPaintHandle);
- const SkPaint* src = reinterpret_cast<SkPaint*>(srcPaintHandle);
+ Paint* dst = reinterpret_cast<Paint*>(dstPaintHandle);
+ const Paint* src = reinterpret_cast<Paint*>(srcPaintHandle);
*dst = *src;
}
@@ -110,10 +111,10 @@
static jint getFlags(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
- SkPaint* nativePaint = GraphicsJNI::getNativePaint(env, paint);
+ Paint* nativePaint = GraphicsJNI::getNativePaint(env, paint);
uint32_t result = nativePaint->getFlags();
result &= ~sFilterBitmapFlag; // Filtering no longer stored in this bit. Mask away.
- if (nativePaint->getFilterLevel() != SkPaint::kNone_FilterLevel) {
+ if (nativePaint->getFilterLevel() != Paint::kNone_FilterLevel) {
result |= sFilterBitmapFlag;
}
return static_cast<jint>(result);
@@ -121,11 +122,11 @@
static void setFlags(JNIEnv* env, jobject paint, jint flags) {
NPE_CHECK_RETURN_VOID(env, paint);
- SkPaint* nativePaint = GraphicsJNI::getNativePaint(env, paint);
+ Paint* nativePaint = GraphicsJNI::getNativePaint(env, paint);
// Instead of modifying 0x02, change the filter level.
nativePaint->setFilterLevel(flags & sFilterBitmapFlag
- ? SkPaint::kLow_FilterLevel
- : SkPaint::kNone_FilterLevel);
+ ? Paint::kLow_FilterLevel
+ : Paint::kNone_FilterLevel);
// Don't pass through filter flag, which is no longer stored in paint's flags.
flags &= ~sFilterBitmapFlag;
// Use the existing value for 0x02.
@@ -137,13 +138,13 @@
static jint getHinting(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
return GraphicsJNI::getNativePaint(env, paint)->getHinting()
- == SkPaint::kNo_Hinting ? 0 : 1;
+ == Paint::kNo_Hinting ? 0 : 1;
}
static void setHinting(JNIEnv* env, jobject paint, jint mode) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setHinting(
- mode == 0 ? SkPaint::kNo_Hinting : SkPaint::kNormal_Hinting);
+ mode == 0 ? Paint::kNo_Hinting : Paint::kNormal_Hinting);
}
static void setAntiAlias(JNIEnv* env, jobject paint, jboolean aa) {
@@ -179,7 +180,7 @@
static void setFilterBitmap(JNIEnv* env, jobject paint, jboolean filterBitmap) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setFilterLevel(
- filterBitmap ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel);
+ filterBitmap ? Paint::kLow_FilterLevel : Paint::kNone_FilterLevel);
}
static void setDither(JNIEnv* env, jobject paint, jboolean dither) {
@@ -188,13 +189,13 @@
}
static jint getStyle(JNIEnv* env, jobject clazz,jlong objHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
return static_cast<jint>(obj->getStyle());
}
static void setStyle(JNIEnv* env, jobject clazz, jlong objHandle, jint styleHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
- SkPaint::Style style = static_cast<SkPaint::Style>(styleHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ Paint::Style style = static_cast<Paint::Style>(styleHandle);
obj->setStyle(style);
}
@@ -243,83 +244,83 @@
}
static jint getStrokeCap(JNIEnv* env, jobject clazz, jlong objHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
return static_cast<jint>(obj->getStrokeCap());
}
static void setStrokeCap(JNIEnv* env, jobject clazz, jlong objHandle, jint capHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
- SkPaint::Cap cap = static_cast<SkPaint::Cap>(capHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ Paint::Cap cap = static_cast<Paint::Cap>(capHandle);
obj->setStrokeCap(cap);
}
static jint getStrokeJoin(JNIEnv* env, jobject clazz, jlong objHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
return static_cast<jint>(obj->getStrokeJoin());
}
static void setStrokeJoin(JNIEnv* env, jobject clazz, jlong objHandle, jint joinHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
- SkPaint::Join join = (SkPaint::Join) joinHandle;
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ Paint::Join join = (Paint::Join) joinHandle;
obj->setStrokeJoin(join);
}
static jboolean getFillPath(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jlong dstHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE;
}
static jlong setShader(JNIEnv* env, jobject clazz, jlong objHandle, jlong shaderHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
return reinterpret_cast<jlong>(obj->setShader(shader));
}
static jlong setColorFilter(JNIEnv* env, jobject clazz, jlong objHandle, jlong filterHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint *>(objHandle);
+ Paint* obj = reinterpret_cast<Paint *>(objHandle);
SkColorFilter* filter = reinterpret_cast<SkColorFilter *>(filterHandle);
return reinterpret_cast<jlong>(obj->setColorFilter(filter));
}
static jlong setXfermode(JNIEnv* env, jobject clazz, jlong objHandle, jlong xfermodeHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
SkXfermode* xfermode = reinterpret_cast<SkXfermode*>(xfermodeHandle);
return reinterpret_cast<jlong>(obj->setXfermode(xfermode));
}
static jlong setPathEffect(JNIEnv* env, jobject clazz, jlong objHandle, jlong effectHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
SkPathEffect* effect = reinterpret_cast<SkPathEffect*>(effectHandle);
return reinterpret_cast<jlong>(obj->setPathEffect(effect));
}
static jlong setMaskFilter(JNIEnv* env, jobject clazz, jlong objHandle, jlong maskfilterHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
SkMaskFilter* maskfilter = reinterpret_cast<SkMaskFilter*>(maskfilterHandle);
return reinterpret_cast<jlong>(obj->setMaskFilter(maskfilter));
}
static jlong setTypeface(JNIEnv* env, jobject clazz, jlong objHandle, jlong typefaceHandle) {
- // TODO: in Paint refactoring, set typeface on android Paint, not SkPaint
+ // TODO: in Paint refactoring, set typeface on android Paint, not Paint
return NULL;
}
static jlong setRasterizer(JNIEnv* env, jobject clazz, jlong objHandle, jlong rasterizerHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
SkAutoTUnref<SkRasterizer> rasterizer(GraphicsJNI::refNativeRasterizer(rasterizerHandle));
return reinterpret_cast<jlong>(obj->setRasterizer(rasterizer));
}
static jint getTextAlign(JNIEnv* env, jobject clazz, jlong objHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
return static_cast<jint>(obj->getTextAlign());
}
static void setTextAlign(JNIEnv* env, jobject clazz, jlong objHandle, jint alignHandle) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
- SkPaint::Align align = static_cast<SkPaint::Align>(alignHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ Paint::Align align = static_cast<Paint::Align>(alignHandle);
obj->setTextAlign(align);
}
@@ -364,7 +365,7 @@
}
static void setTextLocale(JNIEnv* env, jobject clazz, jlong objHandle, jstring locale) {
- SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle);
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
ScopedUtfChars localeChars(env, locale);
char langTag[ULOC_FULLNAME_CAPACITY];
toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, localeChars.c_str());
@@ -376,14 +377,14 @@
static jboolean isElegantTextHeight(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
- SkPaint* obj = GraphicsJNI::getNativePaint(env, paint);
+ Paint* obj = GraphicsJNI::getNativePaint(env, paint);
SkPaintOptionsAndroid paintOpts = obj->getPaintOptionsAndroid();
return paintOpts.getFontVariant() == SkPaintOptionsAndroid::kElegant_Variant;
}
static void setElegantTextHeight(JNIEnv* env, jobject paint, jboolean aa) {
NPE_CHECK_RETURN_VOID(env, paint);
- SkPaint* obj = GraphicsJNI::getNativePaint(env, paint);
+ Paint* obj = GraphicsJNI::getNativePaint(env, paint);
SkPaintOptionsAndroid::FontVariant variant =
aa ? SkPaintOptionsAndroid::kElegant_Variant :
SkPaintOptionsAndroid::kDefault_Variant;
@@ -422,13 +423,23 @@
GraphicsJNI::getNativePaint(env, paint)->setTextSkewX(skewX);
}
- static SkScalar getMetricsInternal(JNIEnv* env, jobject jpaint, SkPaint::FontMetrics *metrics) {
+ static jfloat getLetterSpacing(JNIEnv* env, jobject clazz, jlong paintHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ return paint->getLetterSpacing();
+ }
+
+ static void setLetterSpacing(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat letterSpacing) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ paint->setLetterSpacing(letterSpacing);
+ }
+
+ static SkScalar getMetricsInternal(JNIEnv* env, jobject jpaint, Paint::FontMetrics *metrics) {
const int kElegantTop = 2500;
const int kElegantBottom = -1000;
const int kElegantAscent = 1900;
const int kElegantDescent = -500;
const int kElegantLeading = 0;
- SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
+ Paint* paint = GraphicsJNI::getNativePaint(env, jpaint);
TypefaceImpl* typeface = GraphicsJNI::getNativeTypeface(env, jpaint);
typeface = TypefaceImpl_resolveDefault(typeface);
FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
@@ -456,21 +467,21 @@
static jfloat ascent(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
- SkPaint::FontMetrics metrics;
+ Paint::FontMetrics metrics;
getMetricsInternal(env, paint, &metrics);
return SkScalarToFloat(metrics.fAscent);
}
static jfloat descent(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
- SkPaint::FontMetrics metrics;
+ Paint::FontMetrics metrics;
getMetricsInternal(env, paint, &metrics);
return SkScalarToFloat(metrics.fDescent);
}
static jfloat getFontMetrics(JNIEnv* env, jobject paint, jobject metricsObj) {
NPE_CHECK_RETURN_ZERO(env, paint);
- SkPaint::FontMetrics metrics;
+ Paint::FontMetrics metrics;
SkScalar spacing = getMetricsInternal(env, paint, &metrics);
if (metricsObj) {
@@ -486,7 +497,7 @@
static jint getFontMetricsInt(JNIEnv* env, jobject paint, jobject metricsObj) {
NPE_CHECK_RETURN_ZERO(env, paint);
- SkPaint::FontMetrics metrics;
+ Paint::FontMetrics metrics;
getMetricsInternal(env, paint, &metrics);
int ascent = SkScalarRoundToInt(metrics.fAscent);
@@ -518,7 +529,7 @@
return 0;
}
- SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
+ Paint* paint = GraphicsJNI::getNativePaint(env, jpaint);
const jchar* textArray = env->GetCharArrayElements(text, NULL);
jfloat result = 0;
@@ -547,7 +558,7 @@
}
const jchar* textArray = env->GetStringChars(text, NULL);
- SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
+ Paint* paint = GraphicsJNI::getNativePaint(env, jpaint);
jfloat width = 0;
Layout layout;
@@ -570,7 +581,7 @@
}
const jchar* textArray = env->GetStringChars(text, NULL);
- SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
+ Paint* paint = GraphicsJNI::getNativePaint(env, jpaint);
jfloat width = 0;
Layout layout;
@@ -583,7 +594,7 @@
return width;
}
- static int dotextwidths(JNIEnv* env, SkPaint* paint, TypefaceImpl* typeface, const jchar text[], int count,
+ static int dotextwidths(JNIEnv* env, Paint* paint, TypefaceImpl* typeface, const jchar text[], int count,
jfloatArray widths, jint bidiFlags) {
NPE_CHECK_RETURN_ZERO(env, paint);
NPE_CHECK_RETURN_ZERO(env, text);
@@ -614,7 +625,7 @@
static jint getTextWidths___CIII_F(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jcharArray text,
jint index, jint count, jint bidiFlags, jfloatArray widths) {
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
const jchar* textArray = env->GetCharArrayElements(text, NULL);
count = dotextwidths(env, paint, typeface, textArray + index, count, widths, bidiFlags);
@@ -625,7 +636,7 @@
static jint getTextWidths__StringIII_F(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jstring text,
jint start, jint end, jint bidiFlags, jfloatArray widths) {
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
const jchar* textArray = env->GetStringChars(text, NULL);
int count = dotextwidths(env, paint, typeface, textArray + start, end - start, widths, bidiFlags);
@@ -633,7 +644,7 @@
return count;
}
- static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, TypefaceImpl* typeface, const jchar *text,
+ static jfloat doTextRunAdvances(JNIEnv *env, Paint *paint, TypefaceImpl* typeface, const jchar *text,
jint start, jint count, jint contextCount, jboolean isRtl,
jfloatArray advances, jint advancesIndex) {
NPE_CHECK_RETURN_ZERO(env, paint);
@@ -675,7 +686,7 @@
jlong typefaceHandle,
jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
jboolean isRtl, jfloatArray advances, jint advancesIndex) {
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
jchar* textArray = env->GetCharArrayElements(text, NULL);
jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextIndex,
@@ -688,7 +699,7 @@
jlong typefaceHandle,
jstring text, jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl,
jfloatArray advances, jint advancesIndex) {
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
const jchar* textArray = env->GetStringChars(text, NULL);
jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextStart,
@@ -698,7 +709,7 @@
return result;
}
- static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start,
+ static jint doTextRunCursor(JNIEnv *env, Paint* paint, const jchar *text, jint start,
jint count, jint flags, jint offset, jint opt) {
GraphemeBreak::MoveOpt moveOpt = GraphemeBreak::MoveOpt(opt);
size_t result = GraphemeBreak::getTextRunCursor(text, start, count, offset, moveOpt);
@@ -707,7 +718,7 @@
static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text,
jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) {
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
jchar* textArray = env->GetCharArrayElements(text, NULL);
jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, dir,
offset, cursorOpt);
@@ -717,7 +728,7 @@
static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle, jstring text,
jint contextStart, jint contextEnd, jint dir, jint offset, jint cursorOpt) {
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
const jchar* textArray = env->GetStringChars(text, NULL);
jint result = doTextRunCursor(env, paint, textArray, contextStart,
contextEnd - contextStart, dir, offset, cursorOpt);
@@ -727,7 +738,7 @@
class GetTextFunctor {
public:
- GetTextFunctor(const Layout& layout, SkPath* path, jfloat x, jfloat y, SkPaint* paint,
+ GetTextFunctor(const Layout& layout, SkPath* path, jfloat x, jfloat y, Paint* paint,
uint16_t* glyphs, SkPoint* pos)
: layout(layout), path(path), x(x), y(y), paint(paint), glyphs(glyphs), pos(pos) {
}
@@ -750,13 +761,13 @@
SkPath* path;
jfloat x;
jfloat y;
- SkPaint* paint;
+ Paint* paint;
uint16_t* glyphs;
SkPoint* pos;
SkPath tmpPath;
};
- static void getTextPath(JNIEnv* env, SkPaint* paint, TypefaceImpl* typeface, const jchar* text,
+ static void getTextPath(JNIEnv* env, Paint* paint, TypefaceImpl* typeface, const jchar* text,
jint count, jint bidiFlags, jfloat x, jfloat y, SkPath* path) {
Layout layout;
std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
@@ -766,9 +777,9 @@
SkPoint* pos = new SkPoint[nGlyphs];
x += MinikinUtils::xOffsetForTextAlign(paint, layout);
- SkPaint::Align align = paint->getTextAlign();
- paint->setTextAlign(SkPaint::kLeft_Align);
- paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ Paint::Align align = paint->getTextAlign();
+ paint->setTextAlign(Paint::kLeft_Align);
+ paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
GetTextFunctor f(layout, path, x, y, paint, glyphs, pos);
MinikinUtils::forFontRun(layout, paint, f);
paint->setTextAlign(align);
@@ -779,7 +790,7 @@
static void getTextPath___C(JNIEnv* env, jobject clazz, jlong paintHandle,
jlong typefaceHandle, jint bidiFlags,
jcharArray text, jint index, jint count, jfloat x, jfloat y, jlong pathHandle) {
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
const jchar* textArray = env->GetCharArrayElements(text, NULL);
@@ -790,7 +801,7 @@
static void getTextPath__String(JNIEnv* env, jobject clazz, jlong paintHandle,
jlong typefaceHandle, jint bidiFlags,
jstring text, jint start, jint end, jfloat x, jfloat y, jlong pathHandle) {
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
const jchar* textArray = env->GetStringChars(text, NULL);
@@ -800,7 +811,7 @@
static void setShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat radius,
jfloat dx, jfloat dy, jint color) {
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
if (radius <= 0) {
paint->setLooper(NULL);
}
@@ -811,13 +822,13 @@
}
static jboolean hasShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle) {
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
return paint->getLooper() && paint->getLooper()->asABlurShadow(NULL);
}
- static int breakText(JNIEnv* env, const SkPaint& paint, TypefaceImpl* typeface, const jchar text[],
+ static int breakText(JNIEnv* env, const Paint& paint, TypefaceImpl* typeface, const jchar text[],
int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
- SkPaint::TextBufferDirection textBufferDirection) {
+ Paint::TextBufferDirection textBufferDirection) {
size_t measuredCount = 0;
float measured = 0;
@@ -826,7 +837,7 @@
layout.doLayout(text, 0, count, count, css);
float* advances = new float[count];
layout.getAdvances(advances);
- const bool forwardScan = (textBufferDirection == SkPaint::kForward_TextBufferDirection);
+ const bool forwardScan = (textBufferDirection == Paint::kForward_TextBufferDirection);
for (int i = 0; i < count; i++) {
// traverse in the given direction
int index = forwardScan ? i : (count - i - 1);
@@ -854,16 +865,16 @@
jint index, jint count, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
NPE_CHECK_RETURN_ZERO(env, jtext);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
- SkPaint::TextBufferDirection tbd;
+ Paint::TextBufferDirection tbd;
if (count < 0) {
- tbd = SkPaint::kBackward_TextBufferDirection;
+ tbd = Paint::kBackward_TextBufferDirection;
count = -count;
}
else {
- tbd = SkPaint::kForward_TextBufferDirection;
+ tbd = Paint::kForward_TextBufferDirection;
}
if ((index < 0) || (index + count > env->GetArrayLength(jtext))) {
@@ -883,12 +894,12 @@
jboolean forwards, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
NPE_CHECK_RETURN_ZERO(env, jtext);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
- SkPaint::TextBufferDirection tbd = forwards ?
- SkPaint::kForward_TextBufferDirection :
- SkPaint::kBackward_TextBufferDirection;
+ Paint::TextBufferDirection tbd = forwards ?
+ Paint::kForward_TextBufferDirection :
+ Paint::kBackward_TextBufferDirection;
int count = env->GetStringLength(jtext);
const jchar* text = env->GetStringChars(jtext, NULL);
@@ -898,7 +909,7 @@
}
static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds,
- const SkPaint& paint, TypefaceImpl* typeface, jint bidiFlags) {
+ const Paint& paint, TypefaceImpl* typeface, jint bidiFlags) {
SkRect r;
SkIRect ir;
@@ -917,7 +928,7 @@
static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle,
jstring text, jint start, jint end, jint bidiFlags, jobject bounds) {
- const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);;
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);;
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
const jchar* textArray = env->GetStringChars(text, NULL);
doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags);
@@ -926,7 +937,7 @@
static void getCharArrayBounds(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle,
jcharArray text, jint index, jint count, jint bidiFlags, jobject bounds) {
- const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
const jchar* textArray = env->GetCharArrayElements(text, NULL);
doTextBounds(env, textArray + index, count, bounds, *paint, typeface, bidiFlags);
@@ -937,83 +948,85 @@
};
static JNINativeMethod methods[] = {
- {"finalizer", "(J)V", (void*) SkPaintGlue::finalizer},
- {"native_init","()J", (void*) SkPaintGlue::init},
- {"native_initWithPaint","(J)J", (void*) SkPaintGlue::initWithPaint},
- {"native_reset","(J)V", (void*) SkPaintGlue::reset},
- {"native_set","(JJ)V", (void*) SkPaintGlue::assign},
- {"getFlags","()I", (void*) SkPaintGlue::getFlags},
- {"setFlags","(I)V", (void*) SkPaintGlue::setFlags},
- {"getHinting","()I", (void*) SkPaintGlue::getHinting},
- {"setHinting","(I)V", (void*) SkPaintGlue::setHinting},
- {"setAntiAlias","(Z)V", (void*) SkPaintGlue::setAntiAlias},
- {"setSubpixelText","(Z)V", (void*) SkPaintGlue::setSubpixelText},
- {"setLinearText","(Z)V", (void*) SkPaintGlue::setLinearText},
- {"setUnderlineText","(Z)V", (void*) SkPaintGlue::setUnderlineText},
- {"setStrikeThruText","(Z)V", (void*) SkPaintGlue::setStrikeThruText},
- {"setFakeBoldText","(Z)V", (void*) SkPaintGlue::setFakeBoldText},
- {"setFilterBitmap","(Z)V", (void*) SkPaintGlue::setFilterBitmap},
- {"setDither","(Z)V", (void*) SkPaintGlue::setDither},
- {"native_getStyle","(J)I", (void*) SkPaintGlue::getStyle},
- {"native_setStyle","(JI)V", (void*) SkPaintGlue::setStyle},
- {"getColor","()I", (void*) SkPaintGlue::getColor},
- {"setColor","(I)V", (void*) SkPaintGlue::setColor},
- {"getAlpha","()I", (void*) SkPaintGlue::getAlpha},
- {"setAlpha","(I)V", (void*) SkPaintGlue::setAlpha},
- {"getStrokeWidth","()F", (void*) SkPaintGlue::getStrokeWidth},
- {"setStrokeWidth","(F)V", (void*) SkPaintGlue::setStrokeWidth},
- {"getStrokeMiter","()F", (void*) SkPaintGlue::getStrokeMiter},
- {"setStrokeMiter","(F)V", (void*) SkPaintGlue::setStrokeMiter},
- {"native_getStrokeCap","(J)I", (void*) SkPaintGlue::getStrokeCap},
- {"native_setStrokeCap","(JI)V", (void*) SkPaintGlue::setStrokeCap},
- {"native_getStrokeJoin","(J)I", (void*) SkPaintGlue::getStrokeJoin},
- {"native_setStrokeJoin","(JI)V", (void*) SkPaintGlue::setStrokeJoin},
- {"native_getFillPath","(JJJ)Z", (void*) SkPaintGlue::getFillPath},
- {"native_setShader","(JJ)J", (void*) SkPaintGlue::setShader},
- {"native_setColorFilter","(JJ)J", (void*) SkPaintGlue::setColorFilter},
- {"native_setXfermode","(JJ)J", (void*) SkPaintGlue::setXfermode},
- {"native_setPathEffect","(JJ)J", (void*) SkPaintGlue::setPathEffect},
- {"native_setMaskFilter","(JJ)J", (void*) SkPaintGlue::setMaskFilter},
- {"native_setTypeface","(JJ)J", (void*) SkPaintGlue::setTypeface},
- {"native_setRasterizer","(JJ)J", (void*) SkPaintGlue::setRasterizer},
- {"native_getTextAlign","(J)I", (void*) SkPaintGlue::getTextAlign},
- {"native_setTextAlign","(JI)V", (void*) SkPaintGlue::setTextAlign},
- {"native_setTextLocale","(JLjava/lang/String;)V", (void*) SkPaintGlue::setTextLocale},
- {"isElegantTextHeight","()Z", (void*) SkPaintGlue::isElegantTextHeight},
- {"setElegantTextHeight","(Z)V", (void*) SkPaintGlue::setElegantTextHeight},
- {"getTextSize","()F", (void*) SkPaintGlue::getTextSize},
- {"setTextSize","(F)V", (void*) SkPaintGlue::setTextSize},
- {"getTextScaleX","()F", (void*) SkPaintGlue::getTextScaleX},
- {"setTextScaleX","(F)V", (void*) SkPaintGlue::setTextScaleX},
- {"getTextSkewX","()F", (void*) SkPaintGlue::getTextSkewX},
- {"setTextSkewX","(F)V", (void*) SkPaintGlue::setTextSkewX},
- {"ascent","()F", (void*) SkPaintGlue::ascent},
- {"descent","()F", (void*) SkPaintGlue::descent},
- {"getFontMetrics", "(Landroid/graphics/Paint$FontMetrics;)F", (void*)SkPaintGlue::getFontMetrics},
- {"getFontMetricsInt", "(Landroid/graphics/Paint$FontMetricsInt;)I", (void*)SkPaintGlue::getFontMetricsInt},
- {"native_measureText","([CIII)F", (void*) SkPaintGlue::measureText_CIII},
- {"native_measureText","(Ljava/lang/String;I)F", (void*) SkPaintGlue::measureText_StringI},
- {"native_measureText","(Ljava/lang/String;III)F", (void*) SkPaintGlue::measureText_StringIII},
- {"native_breakText","(JJ[CIIFI[F)I", (void*) SkPaintGlue::breakTextC},
- {"native_breakText","(JJLjava/lang/String;ZFI[F)I", (void*) SkPaintGlue::breakTextS},
- {"native_getTextWidths","(JJ[CIII[F)I", (void*) SkPaintGlue::getTextWidths___CIII_F},
- {"native_getTextWidths","(JJLjava/lang/String;III[F)I", (void*) SkPaintGlue::getTextWidths__StringIII_F},
+ {"finalizer", "(J)V", (void*) PaintGlue::finalizer},
+ {"native_init","()J", (void*) PaintGlue::init},
+ {"native_initWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
+ {"native_reset","(J)V", (void*) PaintGlue::reset},
+ {"native_set","(JJ)V", (void*) PaintGlue::assign},
+ {"getFlags","()I", (void*) PaintGlue::getFlags},
+ {"setFlags","(I)V", (void*) PaintGlue::setFlags},
+ {"getHinting","()I", (void*) PaintGlue::getHinting},
+ {"setHinting","(I)V", (void*) PaintGlue::setHinting},
+ {"setAntiAlias","(Z)V", (void*) PaintGlue::setAntiAlias},
+ {"setSubpixelText","(Z)V", (void*) PaintGlue::setSubpixelText},
+ {"setLinearText","(Z)V", (void*) PaintGlue::setLinearText},
+ {"setUnderlineText","(Z)V", (void*) PaintGlue::setUnderlineText},
+ {"setStrikeThruText","(Z)V", (void*) PaintGlue::setStrikeThruText},
+ {"setFakeBoldText","(Z)V", (void*) PaintGlue::setFakeBoldText},
+ {"setFilterBitmap","(Z)V", (void*) PaintGlue::setFilterBitmap},
+ {"setDither","(Z)V", (void*) PaintGlue::setDither},
+ {"native_getStyle","(J)I", (void*) PaintGlue::getStyle},
+ {"native_setStyle","(JI)V", (void*) PaintGlue::setStyle},
+ {"getColor","()I", (void*) PaintGlue::getColor},
+ {"setColor","(I)V", (void*) PaintGlue::setColor},
+ {"getAlpha","()I", (void*) PaintGlue::getAlpha},
+ {"setAlpha","(I)V", (void*) PaintGlue::setAlpha},
+ {"getStrokeWidth","()F", (void*) PaintGlue::getStrokeWidth},
+ {"setStrokeWidth","(F)V", (void*) PaintGlue::setStrokeWidth},
+ {"getStrokeMiter","()F", (void*) PaintGlue::getStrokeMiter},
+ {"setStrokeMiter","(F)V", (void*) PaintGlue::setStrokeMiter},
+ {"native_getStrokeCap","(J)I", (void*) PaintGlue::getStrokeCap},
+ {"native_setStrokeCap","(JI)V", (void*) PaintGlue::setStrokeCap},
+ {"native_getStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin},
+ {"native_setStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin},
+ {"native_getFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath},
+ {"native_setShader","(JJ)J", (void*) PaintGlue::setShader},
+ {"native_setColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter},
+ {"native_setXfermode","(JJ)J", (void*) PaintGlue::setXfermode},
+ {"native_setPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect},
+ {"native_setMaskFilter","(JJ)J", (void*) PaintGlue::setMaskFilter},
+ {"native_setTypeface","(JJ)J", (void*) PaintGlue::setTypeface},
+ {"native_setRasterizer","(JJ)J", (void*) PaintGlue::setRasterizer},
+ {"native_getTextAlign","(J)I", (void*) PaintGlue::getTextAlign},
+ {"native_setTextAlign","(JI)V", (void*) PaintGlue::setTextAlign},
+ {"native_setTextLocale","(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocale},
+ {"isElegantTextHeight","()Z", (void*) PaintGlue::isElegantTextHeight},
+ {"setElegantTextHeight","(Z)V", (void*) PaintGlue::setElegantTextHeight},
+ {"getTextSize","()F", (void*) PaintGlue::getTextSize},
+ {"setTextSize","(F)V", (void*) PaintGlue::setTextSize},
+ {"getTextScaleX","()F", (void*) PaintGlue::getTextScaleX},
+ {"setTextScaleX","(F)V", (void*) PaintGlue::setTextScaleX},
+ {"getTextSkewX","()F", (void*) PaintGlue::getTextSkewX},
+ {"setTextSkewX","(F)V", (void*) PaintGlue::setTextSkewX},
+ {"native_getLetterSpacing","(J)F", (void*) PaintGlue::getLetterSpacing},
+ {"native_setLetterSpacing","(JF)V", (void*) PaintGlue::setLetterSpacing},
+ {"ascent","()F", (void*) PaintGlue::ascent},
+ {"descent","()F", (void*) PaintGlue::descent},
+ {"getFontMetrics", "(Landroid/graphics/Paint$FontMetrics;)F", (void*)PaintGlue::getFontMetrics},
+ {"getFontMetricsInt", "(Landroid/graphics/Paint$FontMetricsInt;)I", (void*)PaintGlue::getFontMetricsInt},
+ {"native_measureText","([CIII)F", (void*) PaintGlue::measureText_CIII},
+ {"native_measureText","(Ljava/lang/String;I)F", (void*) PaintGlue::measureText_StringI},
+ {"native_measureText","(Ljava/lang/String;III)F", (void*) PaintGlue::measureText_StringIII},
+ {"native_breakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC},
+ {"native_breakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
+ {"native_getTextWidths","(JJ[CIII[F)I", (void*) PaintGlue::getTextWidths___CIII_F},
+ {"native_getTextWidths","(JJLjava/lang/String;III[F)I", (void*) PaintGlue::getTextWidths__StringIII_F},
{"native_getTextRunAdvances","(JJ[CIIIIZ[FI)F",
- (void*) SkPaintGlue::getTextRunAdvances___CIIIIZ_FI},
+ (void*) PaintGlue::getTextRunAdvances___CIIIIZ_FI},
{"native_getTextRunAdvances","(JJLjava/lang/String;IIIIZ[FI)F",
- (void*) SkPaintGlue::getTextRunAdvances__StringIIIIZ_FI},
+ (void*) PaintGlue::getTextRunAdvances__StringIIIIZ_FI},
- {"native_getTextRunCursor", "(J[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C},
+ {"native_getTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
{"native_getTextRunCursor", "(JLjava/lang/String;IIIII)I",
- (void*) SkPaintGlue::getTextRunCursor__String},
- {"native_getTextPath","(JJI[CIIFFJ)V", (void*) SkPaintGlue::getTextPath___C},
- {"native_getTextPath","(JJILjava/lang/String;IIFFJ)V", (void*) SkPaintGlue::getTextPath__String},
+ (void*) PaintGlue::getTextRunCursor__String},
+ {"native_getTextPath","(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
+ {"native_getTextPath","(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
{"nativeGetStringBounds", "(JJLjava/lang/String;IIILandroid/graphics/Rect;)V",
- (void*) SkPaintGlue::getStringBounds },
+ (void*) PaintGlue::getStringBounds },
{"nativeGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V",
- (void*) SkPaintGlue::getCharArrayBounds },
- {"native_setShadowLayer", "(JFFFI)V", (void*)SkPaintGlue::setShadowLayer},
- {"native_hasShadowLayer", "(J)Z", (void*)SkPaintGlue::hasShadowLayer}
+ (void*) PaintGlue::getCharArrayBounds },
+ {"native_setShadowLayer", "(JFFFI)V", (void*)PaintGlue::setShadowLayer},
+ {"native_hasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}
};
static jfieldID req_fieldID(jfieldID id) {
diff --git a/core/jni/android/graphics/Paint.h b/core/jni/android/graphics/Paint.h
new file mode 100644
index 0000000..7235cc4
--- /dev/null
+++ b/core/jni/android/graphics/Paint.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ANDROID_GRAPHICS_PAINT_H
+#define ANDROID_GRAPHICS_PAINT_H
+
+#include <SkPaint.h>
+
+namespace android {
+
+class Paint : public SkPaint {
+public:
+ Paint();
+ Paint(const Paint& paint);
+ ~Paint();
+
+ Paint& operator=(const Paint& other);
+
+ friend bool operator==(const Paint& a, const Paint& b);
+ friend bool operator!=(const Paint& a, const Paint& b) {
+ return !(a == b);
+ }
+
+ void setLetterSpacing(float letterSpacing) {
+ mLetterSpacing = letterSpacing;
+ }
+
+ float getLetterSpacing() const {
+ return mLetterSpacing;
+ }
+
+private:
+ float mLetterSpacing;
+};
+
+} // namespace android
+
+#endif // ANDROID_GRAPHICS_PAINT_H
diff --git a/core/jni/android/graphics/PaintImpl.cpp b/core/jni/android/graphics/PaintImpl.cpp
new file mode 100644
index 0000000..ff2bbc5
--- /dev/null
+++ b/core/jni/android/graphics/PaintImpl.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "Paint.h"
+#include <SkPaint.h>
+
+#define LOG_TAG "Paint"
+#include <cutils/log.h>
+
+namespace android {
+
+Paint::Paint() : SkPaint(),
+ mLetterSpacing(0) {
+}
+
+Paint::Paint(const Paint& paint) : SkPaint(paint),
+ mLetterSpacing(0) {
+}
+
+Paint::~Paint() {
+}
+
+Paint& Paint::operator=(const Paint& other) {
+ SkPaint::operator=(other);
+ mLetterSpacing = other.mLetterSpacing;
+ return *this;
+}
+
+bool operator==(const Paint& a, const Paint& b) {
+ return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b)
+ && a.mLetterSpacing == b.mLetterSpacing;
+}
+
+}
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index bc0c25f..d214575 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
+#include "Canvas.h"
#include "Picture.h"
-#include "SkCanvas.h"
#include "SkStream.h"
namespace android {
@@ -36,12 +36,13 @@
}
}
-SkCanvas* Picture::beginRecording(int width, int height) {
+Canvas* Picture::beginRecording(int width, int height) {
mPicture.reset(NULL);
mRecorder.reset(new SkPictureRecorder);
mWidth = width;
mHeight = height;
- return mRecorder->beginRecording(width, height, NULL, 0);
+ SkCanvas* canvas = mRecorder->beginRecording(width, height, NULL, 0);
+ return Canvas::create_canvas(canvas);
}
void Picture::endRecording() {
@@ -93,14 +94,14 @@
}
}
-void Picture::draw(SkCanvas* canvas) {
+void Picture::draw(Canvas* canvas) {
if (NULL != mRecorder.get()) {
this->endRecording();
SkASSERT(NULL != mPicture.get());
}
if (NULL != mPicture.get()) {
// TODO: remove this const_cast once pictures are immutable
- const_cast<SkPicture*>(mPicture.get())->draw(canvas);
+ const_cast<SkPicture*>(mPicture.get())->draw(canvas->getSkCanvas());
}
}
diff --git a/core/jni/android/graphics/Picture.h b/core/jni/android/graphics/Picture.h
index abb0403..a2e5d4a 100644
--- a/core/jni/android/graphics/Picture.h
+++ b/core/jni/android/graphics/Picture.h
@@ -22,14 +22,13 @@
#include "SkRefCnt.h"
#include "SkTemplates.h"
-class SkCanvas;
-class SkPicture;
-class SkPictureRecorder;
class SkStream;
class SkWStream;
namespace android {
+class Canvas;
+
// Skia's SkPicture class has been split into an SkPictureRecorder
// and an SkPicture. AndroidPicture recreates the functionality
// of the old SkPicture interface by flip-flopping between the two
@@ -38,7 +37,7 @@
public:
explicit Picture(const Picture* src = NULL);
- SkCanvas* beginRecording(int width, int height);
+ Canvas* beginRecording(int width, int height);
void endRecording();
@@ -50,7 +49,7 @@
void serialize(SkWStream* stream) const;
- void draw(SkCanvas* canvas);
+ void draw(Canvas* canvas);
private:
int mWidth;
diff --git a/core/jni/android/graphics/Rasterizer.cpp b/core/jni/android/graphics/Rasterizer.cpp
index a05d19b..2b1aca1 100644
--- a/core/jni/android/graphics/Rasterizer.cpp
+++ b/core/jni/android/graphics/Rasterizer.cpp
@@ -22,6 +22,7 @@
#include "jni.h"
#include "GraphicsJNI.h"
+#include "Paint.h"
#include "SkLayerRasterizer.h"
#include <android_runtime/AndroidRuntime.h>
@@ -79,7 +80,7 @@
static void addLayer(JNIEnv* env, jobject, jlong layerHandle, jlong paintHandle, jfloat dx, jfloat dy) {
NativeLayerRasterizer* nr = reinterpret_cast<NativeLayerRasterizer *>(layerHandle);
- const SkPaint* paint = reinterpret_cast<SkPaint *>(paintHandle);
+ const Paint* paint = reinterpret_cast<Paint *>(paintHandle);
SkASSERT(nr);
SkASSERT(paint);
nr->fBuilder.addLayer(*paint, dx, dy);
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index c71f226..fbb243a 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -50,15 +50,20 @@
///////////////////////////////////////////////////////////////////////////////////////////////
-static void Shader_destructor(JNIEnv* env, jobject o, jlong shaderHandle)
+static void Shader_destructor(JNIEnv* env, jobject o, jlong shaderHandle, jlong shaderWithLMHandle)
{
SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
SkSafeUnref(shader);
+ SkShader* shaderWithLM = reinterpret_cast<SkShader*>(shaderWithLMHandle);
+ SkSafeUnref(shaderWithLM);
}
static jlong Shader_setLocalMatrix(JNIEnv* env, jobject o, jlong shaderHandle,
- jlong matrixHandle)
+ jlong oldLocalMatrixShaderHandle, jlong matrixHandle)
{
+ // The old shader with local matrix is no longer needed, so unref it.
+ SkSafeUnref(reinterpret_cast<SkShader*>(oldLocalMatrixShaderHandle));
+
SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
if (shader) {
@@ -66,7 +71,6 @@
matrix = &SkMatrix::I();
}
SkShader* newShader = SkShader::CreateLocalMatrixShader(shader, *matrix);
- shader->unref();
shader = newShader;
}
return reinterpret_cast<jlong>(shader);
@@ -239,8 +243,8 @@
};
static JNINativeMethod gShaderMethods[] = {
- { "nativeDestructor", "(J)V", (void*)Shader_destructor },
- { "nativeSetLocalMatrix", "(JJ)J", (void*)Shader_setLocalMatrix }
+ { "nativeDestructor", "(JJ)V", (void*)Shader_destructor },
+ { "nativeSetLocalMatrix", "(JJJ)J", (void*)Shader_setLocalMatrix }
};
static JNINativeMethod gBitmapShaderMethods[] = {
diff --git a/core/jni/android/graphics/SkiaCanvas.cpp b/core/jni/android/graphics/SkiaCanvas.cpp
new file mode 100644
index 0000000..61ad398
--- /dev/null
+++ b/core/jni/android/graphics/SkiaCanvas.cpp
@@ -0,0 +1,713 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include "Canvas.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "SkCanvas.h"
+#include "SkClipStack.h"
+#include "SkDevice.h"
+#include "SkDeque.h"
+#include "SkDrawFilter.h"
+#include "SkGraphics.h"
+#include "SkPorterDuff.h"
+#include "SkShader.h"
+#include "SkTArray.h"
+#include "SkTemplates.h"
+
+#include <minikin/Layout.h>
+#include "MinikinSkia.h"
+#include "MinikinUtils.h"
+
+#include "TypefaceImpl.h"
+
+#include "unicode/ubidi.h"
+#include "unicode/ushape.h"
+
+#include <utils/Log.h>
+
+namespace android {
+
+// Holds an SkCanvas reference plus additional native data.
+class SkiaCanvas : public Canvas {
+public:
+ SkiaCanvas(SkBitmap* bitmap);
+
+ SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {
+ SkASSERT(canvas);
+ }
+
+ virtual SkCanvas* getSkCanvas() {
+ return mCanvas.get();
+ }
+
+ virtual void setBitmap(SkBitmap* bitmap, bool copyState);
+
+ virtual bool isOpaque();
+ virtual int width();
+ virtual int height();
+
+ virtual int getSaveCount() const;
+ virtual int save(SkCanvas::SaveFlags flags);
+ virtual void restore();
+ virtual void restoreToCount(int saveCount);
+
+ virtual int saveLayer(float left, float top, float right, float bottom,
+ const SkPaint* paint, SkCanvas::SaveFlags flags);
+ virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+ int alpha, SkCanvas::SaveFlags flags);
+
+ virtual void getMatrix(SkMatrix* outMatrix) const;
+ virtual void setMatrix(const SkMatrix& matrix);
+ virtual void concat(const SkMatrix& matrix);
+ virtual void rotate(float degrees);
+ virtual void scale(float sx, float sy);
+ virtual void skew(float sx, float sy);
+ virtual void translate(float dx, float dy);
+
+ virtual bool getClipBounds(SkRect* outRect) const;
+ virtual bool quickRejectRect(float left, float top, float right, float bottom) const;
+ virtual bool quickRejectPath(const SkPath& path) const;
+ virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+ virtual bool clipPath(const SkPath* path, SkRegion::Op op);
+ virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
+
+ virtual SkDrawFilter* getDrawFilter();
+ virtual void setDrawFilter(SkDrawFilter* drawFilter);
+
+ virtual void drawColor(int color, SkXfermode::Mode mode);
+ virtual void drawPaint(const SkPaint& paint);
+
+ virtual void drawPoint(float x, float y, const SkPaint& paint);
+ virtual void drawPoints(const float* points, int count, const SkPaint& paint);
+ virtual void drawLine(float startX, float startY, float stopX, float stopY,
+ const SkPaint& paint);
+ virtual void drawLines(const float* points, int count, const SkPaint& paint);
+ virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint);
+ virtual void drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint& paint);
+ virtual void drawCircle(float x, float y, float radius, const SkPaint& paint);
+ virtual void drawOval(float left, float top, float right, float bottom, const SkPaint& paint);
+ virtual void drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint);
+ virtual void drawPath(const SkPath& path, const SkPaint& paint);
+ virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+ const float* verts, const float* tex, const int* colors,
+ const uint16_t* indices, int indexCount, const SkPaint& paint);
+
+ virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint);
+ virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint);
+ virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint);
+ virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint);
+
+ virtual void drawText(const uint16_t* text, const float* positions, int count,
+ const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom);
+ virtual void drawPosText(const uint16_t* text, const float* positions, int count,
+ int posCount, const SkPaint& paint);
+ virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint);
+
+ virtual bool drawTextAbsolutePos() const { return true; }
+
+private:
+ struct SaveRec {
+ int saveCount;
+ SkCanvas::SaveFlags saveFlags;
+ };
+
+ void recordPartialSave(SkCanvas::SaveFlags flags);
+ void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount);
+ void applyClips(const SkTArray<SkClipStack::Element>& clips);
+
+ void drawPoints(const float* points, int count, const SkPaint& paint,
+ SkCanvas::PointMode mode);
+ void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
+
+ SkAutoTUnref<SkCanvas> mCanvas;
+ SkAutoTDelete<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
+};
+
+// Construct an SkCanvas from the bitmap.
+static SkCanvas* createCanvas(SkBitmap* bitmap) {
+ if (bitmap) {
+ return SkNEW_ARGS(SkCanvas, (*bitmap));
+ }
+
+ // Create an empty bitmap device to prevent callers from crashing
+ // if they attempt to draw into this canvas.
+ SkBitmap emptyBitmap;
+ return new SkCanvas(emptyBitmap);
+}
+
+Canvas* Canvas::create_canvas(SkBitmap* bitmap) {
+ return new SkiaCanvas(bitmap);
+}
+
+Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) {
+ return new SkiaCanvas(skiaCanvas);
+}
+
+SkiaCanvas::SkiaCanvas(SkBitmap* bitmap) {
+ mCanvas.reset(createCanvas(bitmap));
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Replace Bitmap
+// ----------------------------------------------------------------------------
+
+class ClipCopier : public SkCanvas::ClipVisitor {
+public:
+ ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
+
+ virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) {
+ m_dstCanvas->clipRect(rect, op, antialias);
+ }
+ virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) {
+ m_dstCanvas->clipRRect(rrect, op, antialias);
+ }
+ virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) {
+ m_dstCanvas->clipPath(path, op, antialias);
+ }
+
+private:
+ SkCanvas* m_dstCanvas;
+};
+
+void SkiaCanvas::setBitmap(SkBitmap* bitmap, bool copyState) {
+ SkCanvas* newCanvas = createCanvas(bitmap);
+ SkASSERT(newCanvas);
+
+ if (copyState) {
+ // Copy the canvas matrix & clip state.
+ newCanvas->setMatrix(mCanvas->getTotalMatrix());
+ if (NULL != mCanvas->getDevice() && NULL != newCanvas->getDevice()) {
+ ClipCopier copier(newCanvas);
+ mCanvas->replayClips(&copier);
+ }
+ }
+
+ // unrefs the existing canvas
+ mCanvas.reset(newCanvas);
+
+ // clean up the old save stack
+ mSaveStack.reset(NULL);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations
+// ----------------------------------------------------------------------------
+
+bool SkiaCanvas::isOpaque() {
+ return mCanvas->getDevice()->accessBitmap(false).isOpaque();
+}
+
+int SkiaCanvas::width() {
+ return mCanvas->getBaseLayerSize().width();
+}
+
+int SkiaCanvas::height() {
+ return mCanvas->getBaseLayerSize().height();
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Save (layer)
+// ----------------------------------------------------------------------------
+
+int SkiaCanvas::getSaveCount() const {
+ return mCanvas->getSaveCount();
+}
+
+int SkiaCanvas::save(SkCanvas::SaveFlags flags) {
+ int count = mCanvas->save();
+ recordPartialSave(flags);
+ return count;
+}
+
+void SkiaCanvas::restore() {
+ const SaveRec* rec = (NULL == mSaveStack.get())
+ ? NULL
+ : static_cast<SaveRec*>(mSaveStack->back());
+ int currentSaveCount = mCanvas->getSaveCount() - 1;
+ SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
+
+ if (NULL == rec || rec->saveCount != currentSaveCount) {
+ // Fast path - no record for this frame.
+ mCanvas->restore();
+ return;
+ }
+
+ bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
+ bool preserveClip = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
+
+ SkMatrix savedMatrix;
+ if (preserveMatrix) {
+ savedMatrix = mCanvas->getTotalMatrix();
+ }
+
+ SkTArray<SkClipStack::Element> savedClips;
+ if (preserveClip) {
+ saveClipsForFrame(savedClips, currentSaveCount);
+ }
+
+ mCanvas->restore();
+
+ if (preserveMatrix) {
+ mCanvas->setMatrix(savedMatrix);
+ }
+
+ if (preserveClip && !savedClips.empty()) {
+ applyClips(savedClips);
+ }
+
+ mSaveStack->pop_back();
+}
+
+void SkiaCanvas::restoreToCount(int restoreCount) {
+ while (mCanvas->getSaveCount() > restoreCount) {
+ this->restore();
+ }
+}
+
+int SkiaCanvas::saveLayer(float left, float top, float right, float bottom,
+ const SkPaint* paint, SkCanvas::SaveFlags flags) {
+ SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+ int count = mCanvas->saveLayer(&bounds, paint, flags | SkCanvas::kMatrixClip_SaveFlag);
+ recordPartialSave(flags);
+ return count;
+}
+
+int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom,
+ int alpha, SkCanvas::SaveFlags flags) {
+ SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+ int count = mCanvas->saveLayerAlpha(&bounds, alpha, flags | SkCanvas::kMatrixClip_SaveFlag);
+ recordPartialSave(flags);
+ return count;
+}
+
+// ----------------------------------------------------------------------------
+// functions to emulate legacy SaveFlags (i.e. independent matrix/clip flags)
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::recordPartialSave(SkCanvas::SaveFlags flags) {
+ // A partial save is a save operation which doesn't capture the full canvas state.
+ // (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
+
+ // Mask-out non canvas state bits.
+ flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
+
+ if (SkCanvas::kMatrixClip_SaveFlag == flags) {
+ // not a partial save.
+ return;
+ }
+
+ if (NULL == mSaveStack.get()) {
+ mSaveStack.reset(SkNEW_ARGS(SkDeque, (sizeof(struct SaveRec), 8)));
+ }
+
+ SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
+ // Store the save counter in the SkClipStack domain.
+ // (0-based, equal to the number of save ops on the stack).
+ rec->saveCount = mCanvas->getSaveCount() - 1;
+ rec->saveFlags = flags;
+}
+
+void SkiaCanvas::saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount) {
+ SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
+ SkClipStack::Iter::kTop_IterStart);
+ while (const SkClipStack::Element* elem = clipIterator.next()) {
+ if (elem->getSaveCount() < frameSaveCount) {
+ // done with the current frame.
+ break;
+ }
+ SkASSERT(elem->getSaveCount() == frameSaveCount);
+ clips.push_back(*elem);
+ }
+}
+
+void SkiaCanvas::applyClips(const SkTArray<SkClipStack::Element>& clips) {
+ ClipCopier clipCopier(mCanvas);
+
+ // The clip stack stores clips in device space.
+ SkMatrix origMatrix = mCanvas->getTotalMatrix();
+ mCanvas->resetMatrix();
+
+ // We pushed the clips in reverse order.
+ for (int i = clips.count() - 1; i >= 0; --i) {
+ clips[i].replay(&clipCopier);
+ }
+
+ mCanvas->setMatrix(origMatrix);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Matrix
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::getMatrix(SkMatrix* outMatrix) const {
+ *outMatrix = mCanvas->getTotalMatrix();
+}
+
+void SkiaCanvas::setMatrix(const SkMatrix& matrix) {
+ mCanvas->setMatrix(matrix);
+}
+
+void SkiaCanvas::concat(const SkMatrix& matrix) {
+ mCanvas->concat(matrix);
+}
+
+void SkiaCanvas::rotate(float degrees) {
+ mCanvas->rotate(degrees);
+}
+
+void SkiaCanvas::scale(float sx, float sy) {
+ mCanvas->scale(sx, sy);
+}
+
+void SkiaCanvas::skew(float sx, float sy) {
+ mCanvas->skew(sx, sy);
+}
+
+void SkiaCanvas::translate(float dx, float dy) {
+ mCanvas->translate(dx, dy);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Clips
+// ----------------------------------------------------------------------------
+
+// This function is a mirror of SkCanvas::getClipBounds except that it does
+// not outset the edge of the clip to account for anti-aliasing. There is
+// a skia bug to investigate pushing this logic into back into skia.
+// (see https://code.google.com/p/skia/issues/detail?id=1303)
+bool SkiaCanvas::getClipBounds(SkRect* outRect) const {
+ SkIRect ibounds;
+ if (!mCanvas->getClipDeviceBounds(&ibounds)) {
+ return false;
+ }
+
+ SkMatrix inverse;
+ // if we can't invert the CTM, we can't return local clip bounds
+ if (!mCanvas->getTotalMatrix().invert(&inverse)) {
+ if (outRect) {
+ outRect->setEmpty();
+ }
+ return false;
+ }
+
+ if (NULL != outRect) {
+ SkRect r = SkRect::Make(ibounds);
+ inverse.mapRect(outRect, r);
+ }
+ return true;
+}
+
+bool SkiaCanvas::quickRejectRect(float left, float top, float right, float bottom) const {
+ SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+ return mCanvas->quickReject(bounds);
+}
+
+bool SkiaCanvas::quickRejectPath(const SkPath& path) const {
+ return mCanvas->quickReject(path);
+}
+
+bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ mCanvas->clipRect(rect, op);
+ return mCanvas->isClipEmpty();
+}
+
+bool SkiaCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
+ mCanvas->clipPath(*path, op);
+ return mCanvas->isClipEmpty();
+}
+
+bool SkiaCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
+ SkPath rgnPath;
+ if (region->getBoundaryPath(&rgnPath)) {
+ // The region is specified in device space.
+ SkMatrix savedMatrix = mCanvas->getTotalMatrix();
+ mCanvas->resetMatrix();
+ mCanvas->clipPath(rgnPath, op);
+ mCanvas->setMatrix(savedMatrix);
+ } else {
+ mCanvas->clipRect(SkRect::MakeEmpty(), op);
+ }
+ return mCanvas->isClipEmpty();
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Filters
+// ----------------------------------------------------------------------------
+
+SkDrawFilter* SkiaCanvas::getDrawFilter() {
+ return mCanvas->getDrawFilter();
+}
+
+void SkiaCanvas::setDrawFilter(SkDrawFilter* drawFilter) {
+ mCanvas->setDrawFilter(drawFilter);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawColor(int color, SkXfermode::Mode mode) {
+ mCanvas->drawColor(color, mode);
+}
+
+void SkiaCanvas::drawPaint(const SkPaint& paint) {
+ mCanvas->drawPaint(paint);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations: Geometry
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint,
+ SkCanvas::PointMode mode) {
+ // convert the floats into SkPoints
+ count >>= 1; // now it is the number of points
+ SkAutoSTMalloc<32, SkPoint> storage(count);
+ SkPoint* pts = storage.get();
+ for (int i = 0; i < count; i++) {
+ pts[i].set(points[0], points[1]);
+ points += 2;
+ }
+ mCanvas->drawPoints(mode, count, pts, paint);
+}
+
+
+void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) {
+ mCanvas->drawPoint(x, y, paint);
+}
+
+void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
+ this->drawPoints(points, count, paint, SkCanvas::kPoints_PointMode);
+}
+
+void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY,
+ const SkPaint& paint) {
+ mCanvas->drawLine(startX, startY, stopX, stopY, paint);
+}
+
+void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
+ this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
+}
+
+void SkiaCanvas::drawRect(float left, float top, float right, float bottom,
+ const SkPaint& paint) {
+ mCanvas->drawRectCoords(left, top, right, bottom, paint);
+
+}
+
+void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint& paint) {
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ mCanvas->drawRoundRect(rect, rx, ry, paint);
+}
+
+void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
+ mCanvas->drawCircle(x, y, radius, paint);
+}
+
+void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
+ SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+ mCanvas->drawOval(oval, paint);
+}
+
+void SkiaCanvas::drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
+ SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
+ mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint);
+}
+
+void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+ mCanvas->drawPath(path, paint);
+}
+
+void SkiaCanvas::drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+ const float* verts, const float* texs, const int* colors,
+ const uint16_t* indices, int indexCount, const SkPaint& paint) {
+#ifndef SK_SCALAR_IS_FLOAT
+ SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid");
+#endif
+ const int ptCount = vertexCount >> 1;
+ mCanvas->drawVertices(vertexMode, ptCount, (SkPoint*)verts, (SkPoint*)texs,
+ (SkColor*)colors, NULL, indices, indexCount, paint);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations: Bitmaps
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
+ mCanvas->drawBitmap(bitmap, left, top, paint);
+}
+
+void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
+ mCanvas->drawBitmapMatrix(bitmap, matrix, paint);
+}
+
+void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) {
+ SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
+ SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+ mCanvas->drawBitmapRectToRect(bitmap, &srcRect, dstRect, paint);
+}
+
+void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) {
+
+ const int ptCount = (meshWidth + 1) * (meshHeight + 1);
+ const int indexCount = meshWidth * meshHeight * 6;
+
+ /* Our temp storage holds 2 or 3 arrays.
+ texture points [ptCount * sizeof(SkPoint)]
+ optionally vertex points [ptCount * sizeof(SkPoint)] if we need a
+ copy to convert from float to fixed
+ indices [ptCount * sizeof(uint16_t)]
+ */
+ ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[]
+ storageSize += indexCount * sizeof(uint16_t); // indices[]
+
+
+#ifndef SK_SCALAR_IS_FLOAT
+ SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid");
+#endif
+ SkAutoMalloc storage(storageSize);
+ SkPoint* texs = (SkPoint*)storage.get();
+ uint16_t* indices = (uint16_t*)(texs + ptCount);
+
+ // cons up texture coordinates and indices
+ {
+ const SkScalar w = SkIntToScalar(bitmap.width());
+ const SkScalar h = SkIntToScalar(bitmap.height());
+ const SkScalar dx = w / meshWidth;
+ const SkScalar dy = h / meshHeight;
+
+ SkPoint* texsPtr = texs;
+ SkScalar y = 0;
+ for (int i = 0; i <= meshHeight; i++) {
+ if (i == meshHeight) {
+ y = h; // to ensure numerically we hit h exactly
+ }
+ SkScalar x = 0;
+ for (int j = 0; j < meshWidth; j++) {
+ texsPtr->set(x, y);
+ texsPtr += 1;
+ x += dx;
+ }
+ texsPtr->set(w, y);
+ texsPtr += 1;
+ y += dy;
+ }
+ SkASSERT(texsPtr - texs == ptCount);
+ }
+
+ // cons up indices
+ {
+ uint16_t* indexPtr = indices;
+ int index = 0;
+ for (int i = 0; i < meshHeight; i++) {
+ for (int j = 0; j < meshWidth; j++) {
+ // lower-left triangle
+ *indexPtr++ = index;
+ *indexPtr++ = index + meshWidth + 1;
+ *indexPtr++ = index + meshWidth + 2;
+ // upper-right triangle
+ *indexPtr++ = index;
+ *indexPtr++ = index + meshWidth + 2;
+ *indexPtr++ = index + 1;
+ // bump to the next cell
+ index += 1;
+ }
+ // bump to the next row
+ index += 1;
+ }
+ SkASSERT(indexPtr - indices == indexCount);
+ SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize);
+ }
+
+ // double-check that we have legal indices
+#ifdef SK_DEBUG
+ {
+ for (int i = 0; i < indexCount; i++) {
+ SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
+ }
+ }
+#endif
+
+ // cons-up a shader for the bitmap
+ SkPaint tmpPaint;
+ if (paint) {
+ tmpPaint = *paint;
+ }
+ SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+ SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode);
+ SkSafeUnref(tmpPaint.setShader(shader));
+
+ mCanvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, (SkPoint*)vertices,
+ texs, (const SkColor*)colors, NULL, indices,
+ indexCount, tmpPaint);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations: Text
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawText(const uint16_t* text, const float* positions, int count,
+ const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom) {
+ // Set align to left for drawing, as we don't want individual
+ // glyphs centered or right-aligned; the offset above takes
+ // care of all alignment.
+ SkPaint paintCopy(paint);
+ paintCopy.setTextAlign(SkPaint::kLeft_Align);
+
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paintCopy);
+}
+
+void SkiaCanvas::drawPosText(const uint16_t* text, const float* positions, int count, int posCount,
+ const SkPaint& paint) {
+ SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
+ int indx;
+ for (indx = 0; indx < posCount; indx++) {
+ posPtr[indx].fX = positions[indx << 1];
+ posPtr[indx].fY = positions[(indx << 1) + 1];
+ }
+
+ SkPaint paintCopy(paint);
+ paintCopy.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ mCanvas->drawPosText(text, count, posPtr, paintCopy);
+
+ delete[] posPtr;
+}
+
+void SkiaCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) {
+ mCanvas->drawTextOnPathHV(glyphs, count, path, hOffset, vOffset, paint);
+}
+
+} // namespace android
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index 3812c27..9436a47 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -19,9 +19,9 @@
#include <android_runtime/AndroidRuntime.h>
#include <vector>
+#include "Canvas.h"
#include "CreateJavaOutputStreamAdaptor.h"
-#include "SkCanvas.h"
#include "SkDocument.h"
#include "SkPicture.h"
#include "SkPictureRecorder.h"
@@ -132,8 +132,9 @@
jint pageWidth, jint pageHeight,
jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
- return reinterpret_cast<jlong>(document->startPage(pageWidth, pageHeight,
- contentLeft, contentTop, contentRight, contentBottom));
+ SkCanvas* canvas = document->startPage(pageWidth, pageHeight,
+ contentLeft, contentTop, contentRight, contentBottom);
+ return reinterpret_cast<jlong>(Canvas::create_canvas(canvas));
}
static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) {
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
new file mode 100644
index 0000000..0007912
--- /dev/null
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -0,0 +1,746 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "Canvas.h"
+#include "SkDrawFilter.h"
+#include "SkGraphics.h"
+#include "SkPorterDuff.h"
+#include "Paint.h"
+#include "TypefaceImpl.h"
+
+#include <minikin/Layout.h>
+#include "MinikinSkia.h"
+#include "MinikinUtils.h"
+
+namespace android {
+
+namespace CanvasJNI {
+
+static Canvas* get_canvas(jlong canvasHandle) {
+ return reinterpret_cast<Canvas*>(canvasHandle);
+}
+
+static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) {
+ delete get_canvas(canvasHandle);
+}
+
+// Native wrapper constructor used by Canvas(Bitmap)
+static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
+}
+
+// Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
+// optionally copying canvas matrix & clip state.
+static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+ jboolean copyState) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ get_canvas(canvasHandle)->setBitmap(bitmap, copyState);
+}
+
+static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
+ return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
+}
+
+static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
+ return static_cast<jint>(get_canvas(canvasHandle)->width());
+}
+
+static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
+ return static_cast<jint>(get_canvas(canvasHandle)->height());
+}
+
+static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
+ return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
+}
+
+static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
+ SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+ return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
+}
+
+static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
+ jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+ return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
+}
+
+static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
+ jfloat r, jfloat b, jint alpha, jint flagsHandle) {
+ SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+ return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
+}
+
+static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
+ Canvas* canvas = get_canvas(canvasHandle);
+ if (canvas->getSaveCount() <= 1) { // cannot restore anymore
+ doThrowISE(env, "Underflow in restore");
+ return;
+ }
+ canvas->restore();
+}
+
+static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount) {
+ Canvas* canvas = get_canvas(canvasHandle);
+ if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
+ doThrowIAE(env, "Underflow in restoreToCount");
+ return;
+ }
+ canvas->restoreToCount(restoreCount);
+}
+
+static void getCTM(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
+ SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+ get_canvas(canvasHandle)->getMatrix(matrix);
+}
+
+static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
+ const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+ get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I());
+}
+
+static void concat(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
+ const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+ get_canvas(canvasHandle)->concat(*matrix);
+}
+
+static void rotate(JNIEnv*, jobject, jlong canvasHandle, jfloat degrees) {
+ get_canvas(canvasHandle)->rotate(degrees);
+}
+
+static void scale(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
+ get_canvas(canvasHandle)->scale(sx, sy);
+}
+
+static void skew(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
+ get_canvas(canvasHandle)->skew(sx, sy);
+}
+
+static void translate(JNIEnv*, jobject, jlong canvasHandle, jfloat dx, jfloat dy) {
+ get_canvas(canvasHandle)->translate(dx, dy);
+}
+
+static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) {
+ SkRect r;
+ SkIRect ir;
+ bool result = get_canvas(canvasHandle)->getClipBounds(&r);
+
+ if (!result) {
+ r.setEmpty();
+ }
+ r.round(&ir);
+
+ (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
+ return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean quickRejectRect(JNIEnv* env, jobject, jlong canvasHandle,
+ jfloat left, jfloat top, jfloat right, jfloat bottom) {
+ bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom);
+ return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean quickRejectPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle) {
+ SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+ bool result = get_canvas(canvasHandle)->quickRejectPath(*path);
+ return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle, jfloat l, jfloat t,
+ jfloat r, jfloat b, jint opHandle) {
+ SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
+ bool emptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b, op);
+ return emptyClip ? JNI_FALSE : JNI_TRUE;
+}
+
+static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
+ jint opHandle) {
+ SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+ SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
+ bool emptyClip = get_canvas(canvasHandle)->clipPath(path, op);
+ return emptyClip ? JNI_FALSE : JNI_TRUE;
+}
+
+static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong deviceRgnHandle,
+ jint opHandle) {
+ SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
+ SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
+ bool emptyClip = get_canvas(canvasHandle)->clipRegion(deviceRgn, op);
+ return emptyClip ? JNI_FALSE : JNI_TRUE;
+}
+
+static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
+ SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle);
+ get_canvas(canvasHandle)->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
+}
+
+static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawPaint(*paint);
+}
+
+static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
+ jlong paintHandle) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawPoint(x, y, *paint);
+}
+
+static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
+ jint offset, jint count, jlong paintHandle) {
+ NPE_CHECK_RETURN_VOID(env, jptsArray);
+ AutoJavaFloatArray autoPts(env, jptsArray);
+ float* floats = autoPts.ptr();
+ const int length = autoPts.length();
+
+ if ((offset | count) < 0 || offset + count > length) {
+ doThrowAIOOBE(env);
+ return;
+ }
+
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint);
+}
+
+static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
+ jfloat stopX, jfloat stopY, jlong paintHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
+}
+
+static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
+ jint offset, jint count, jlong paintHandle) {
+ NPE_CHECK_RETURN_VOID(env, jptsArray);
+ AutoJavaFloatArray autoPts(env, jptsArray);
+ float* floats = autoPts.ptr();
+ const int length = autoPts.length();
+
+ if ((offset | count) < 0 || offset + count > length) {
+ doThrowAIOOBE(env);
+ return;
+ }
+
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint);
+}
+
+static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jlong paintHandle) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
+}
+
+static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint);
+}
+
+static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy,
+ jfloat radius, jlong paintHandle) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint);
+}
+
+static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jlong paintHandle) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
+}
+
+static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
+ jboolean useCenter, jlong paintHandle) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle,
+ useCenter, *paint);
+}
+
+static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
+ jlong paintHandle) {
+ const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawPath(*path, *paint);
+}
+
+static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
+ jint modeHandle, jint vertexCount,
+ jfloatArray jverts, jint vertIndex,
+ jfloatArray jtexs, jint texIndex,
+ jintArray jcolors, jint colorIndex,
+ jshortArray jindices, jint indexIndex,
+ jint indexCount, jlong paintHandle) {
+ AutoJavaFloatArray vertA(env, jverts, vertIndex + vertexCount);
+ AutoJavaFloatArray texA(env, jtexs, texIndex + vertexCount);
+ AutoJavaIntArray colorA(env, jcolors, colorIndex + vertexCount);
+ AutoJavaShortArray indexA(env, jindices, indexIndex + indexCount);
+
+ const float* verts = vertA.ptr() + vertIndex;
+ const float* texs = texA.ptr() + vertIndex;
+ const int* colors = NULL;
+ const uint16_t* indices = NULL;
+
+ if (jcolors != NULL) {
+ colors = colorA.ptr() + colorIndex;
+ }
+ if (jindices != NULL) {
+ indices = (const uint16_t*)(indexA.ptr() + indexIndex);
+ }
+
+ SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawVertices(mode, vertexCount, verts, texs, colors,
+ indices, indexCount, *paint);
+}
+
+static void drawBitmap(JNIEnv* env, jobject jcanvas, jlong canvasHandle, jlong bitmapHandle,
+ jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
+ jint screenDensity, jint bitmapDensity) {
+ Canvas* canvas = get_canvas(canvasHandle);
+ const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+ if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
+ if (screenDensity != 0 && screenDensity != bitmapDensity) {
+ Paint filteredPaint;
+ if (paint) {
+ filteredPaint = *paint;
+ }
+ filteredPaint.setFilterLevel(Paint::kLow_FilterLevel);
+ canvas->drawBitmap(*bitmap, left, top, &filteredPaint);
+ } else {
+ canvas->drawBitmap(*bitmap, left, top, paint);
+ }
+ } else {
+ canvas->save(SkCanvas::kMatrixClip_SaveFlag);
+ SkScalar scale = canvasDensity / (float)bitmapDensity;
+ canvas->translate(left, top);
+ canvas->scale(scale, scale);
+
+ Paint filteredPaint;
+ if (paint) {
+ filteredPaint = *paint;
+ }
+ filteredPaint.setFilterLevel(Paint::kLow_FilterLevel);
+
+ canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
+ canvas->restore();
+ }
+}
+
+static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+ jlong matrixHandle, jlong paintHandle) {
+ const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawBitmap(*bitmap, *matrix, paint);
+}
+
+static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+ float srcLeft, float srcTop, float srcRight, float srcBottom,
+ float dstLeft, float dstTop, float dstRight, float dstBottom,
+ jlong paintHandle, jint screenDensity, jint bitmapDensity) {
+ Canvas* canvas = get_canvas(canvasHandle);
+ const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+ if (screenDensity != 0 && screenDensity != bitmapDensity) {
+ Paint filteredPaint;
+ if (paint) {
+ filteredPaint = *paint;
+ }
+ filteredPaint.setFilterLevel(Paint::kLow_FilterLevel);
+ canvas->drawBitmap(*bitmap, srcLeft, srcTop, srcRight, srcBottom,
+ dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
+ } else {
+ canvas->drawBitmap(*bitmap, srcLeft, srcTop, srcRight, srcBottom,
+ dstLeft, dstTop, dstRight, dstBottom, paint);
+ }
+}
+
+static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
+ jintArray jcolors, jint offset, jint stride,
+ jfloat x, jfloat y, jint width, jint height,
+ jboolean hasAlpha, jlong paintHandle) {
+ // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
+ // correct the alphaType to kOpaque_SkAlphaType.
+ SkImageInfo info = SkImageInfo::Make(width, height,
+ hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+ kPremul_SkAlphaType);
+ SkBitmap bitmap;
+ if (!bitmap.allocPixels(info)) {
+ return;
+ }
+
+ if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, bitmap)) {
+ return;
+ }
+
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawBitmap(bitmap, x, y, paint);
+}
+
+static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+ jint meshWidth, jint meshHeight, jfloatArray jverts,
+ jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
+ const int ptCount = (meshWidth + 1) * (meshHeight + 1);
+ AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
+ AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
+
+ const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawBitmapMesh(*bitmap, meshWidth, meshHeight,
+ vertA.ptr(), colorA.ptr(), paint);
+}
+
+class DrawTextFunctor {
+public:
+ DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
+ const SkPaint& paint, float x, float y, MinikinRect& bounds)
+ : layout(layout), canvas(canvas), glyphs(glyphs), pos(pos), paint(paint),
+ x(x), y(y), bounds(bounds) { }
+
+ void operator()(size_t start, size_t end) {
+ if (canvas->drawTextAbsolutePos()) {
+ for (size_t i = start; i < end; i++) {
+ glyphs[i] = layout.getGlyphId(i);
+ pos[2 * i] = x + layout.getX(i);
+ pos[2 * i + 1] = y + layout.getY(i);
+ }
+ } else {
+ for (size_t i = start; i < end; i++) {
+ glyphs[i] = layout.getGlyphId(i);
+ pos[2 * i] = layout.getX(i);
+ pos[2 * i + 1] = layout.getY(i);
+ }
+ }
+
+ size_t glyphCount = end - start;
+ canvas->drawText(glyphs + start, pos + (2 * start), glyphCount, paint, x, y,
+ bounds.mLeft , bounds.mTop , bounds.mRight , bounds.mBottom);
+ }
+private:
+ const Layout& layout;
+ Canvas* canvas;
+ uint16_t* glyphs;
+ float* pos;
+ const SkPaint& paint;
+ float x;
+ float y;
+ MinikinRect& bounds;
+};
+
+// Same values used by Skia
+#define kStdStrikeThru_Offset (-6.0f / 21.0f)
+#define kStdUnderline_Offset (1.0f / 9.0f)
+#define kStdUnderline_Thickness (1.0f / 18.0f)
+
+void drawTextDecorations(Canvas* canvas, float x, float y, float length, const SkPaint& paint) {
+ uint32_t flags;
+ SkDrawFilter* drawFilter = canvas->getDrawFilter();
+ if (drawFilter) {
+ SkPaint paintCopy(paint);
+ drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+ flags = paintCopy.getFlags();
+ } else {
+ flags = paint.getFlags();
+ }
+ if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+ SkScalar left = x;
+ SkScalar right = x + length;
+ float textSize = paint.getTextSize();
+ float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
+ if (flags & SkPaint::kUnderlineText_Flag) {
+ SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
+ SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
+ canvas->drawRect(left, top, right, bottom, paint);
+ }
+ if (flags & SkPaint::kStrikeThruText_Flag) {
+ SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
+ SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
+ canvas->drawRect(left, top, right, bottom, paint);
+ }
+ }
+}
+
+void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int contextCount,
+ float x, float y, int bidiFlags, const Paint& origPaint, TypefaceImpl* typeface) {
+ // minikin may modify the original paint
+ Paint paint(origPaint);
+
+ Layout layout;
+ std::string css = MinikinUtils::setLayoutProperties(&layout, &paint, bidiFlags, typeface);
+ layout.doLayout(text, start, count, contextCount, css);
+
+ size_t nGlyphs = layout.nGlyphs();
+ uint16_t* glyphs = new uint16_t[nGlyphs];
+ float* pos = new float[nGlyphs * 2];
+
+ x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
+
+ MinikinRect bounds;
+ layout.getBounds(&bounds);
+
+ DrawTextFunctor f(layout, canvas, glyphs, pos, paint, x, y, bounds);
+ MinikinUtils::forFontRun(layout, &paint, f);
+
+ drawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
+
+ delete[] glyphs;
+ delete[] pos;
+}
+
+static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
+ jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
+ jlong paintHandle, jlong typefaceHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+ jchar* jchars = env->GetCharArrayElements(text, NULL);
+ drawText(get_canvas(canvasHandle), jchars + index, 0, count, count, x, y,
+ bidiFlags, *paint, typeface);
+ env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
+}
+
+static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
+ jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
+ jlong paintHandle, jlong typefaceHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+ const int count = end - start;
+ const jchar* jchars = env->GetStringChars(text, NULL);
+ drawText(get_canvas(canvasHandle), jchars + start, 0, count, count, x, y,
+ bidiFlags, *paint, typeface);
+ env->ReleaseStringChars(text, jchars);
+}
+
+static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
+ jint count, jint contextIndex, jint contextCount, jfloat x, jfloat y,
+ jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+ const int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
+ jchar* jchars = env->GetCharArrayElements(text, NULL);
+ drawText(get_canvas(canvasHandle), jchars + contextIndex, index - contextIndex, count,
+ contextCount, x, y, bidiFlags, *paint, typeface);
+ env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
+}
+
+static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring text,
+ jint start, jint end, jint contextStart, jint contextEnd,
+ jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
+ jlong typefaceHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+ int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
+ jint count = end - start;
+ jint contextCount = contextEnd - contextStart;
+ const jchar* jchars = env->GetStringChars(text, NULL);
+ drawText(get_canvas(canvasHandle), jchars + contextStart, start - contextStart, count,
+ contextCount, x, y, bidiFlags, *paint, typeface);
+ env->ReleaseStringChars(text, jchars);
+}
+
+static void drawPosTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
+ jint index, jint count, jfloatArray pos, jlong paintHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ jchar* jchars = text ? env->GetCharArrayElements(text, NULL) : NULL;
+ float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
+ int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
+
+ get_canvas(canvasHandle)->drawPosText(jchars + index, posArray, count << 1, posCount, *paint);
+
+ if (text) {
+ env->ReleaseCharArrayElements(text, jchars, 0);
+ }
+ if (pos) {
+ env->ReleaseFloatArrayElements(pos, posArray, 0);
+ }
+}
+
+
+static void drawPosTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
+ jfloatArray pos, jlong paintHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const jchar* jchars = text ? env->GetStringChars(text, NULL) : NULL;
+ int byteLength = text ? env->GetStringLength(text) : 0;
+ float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
+ int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
+
+ get_canvas(canvasHandle)->drawPosText(jchars , posArray, byteLength << 1, posCount, *paint);
+
+ if (text) {
+ env->ReleaseStringChars(text, jchars);
+ }
+ if (pos) {
+ env->ReleaseFloatArrayElements(pos, posArray, 0);
+ }
+}
+
+class DrawTextOnPathFunctor {
+public:
+ DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
+ float vOffset, const Paint& paint, const SkPath& path)
+ : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
+ paint(paint), path(path) {
+ }
+ void operator()(size_t start, size_t end) {
+ uint16_t glyphs[1];
+ for (size_t i = start; i < end; i++) {
+ glyphs[0] = layout.getGlyphId(i);
+ float x = hOffset + layout.getX(i);
+ float y = vOffset + layout.getY(i);
+ canvas->drawTextOnPath(glyphs, 1, path, x, y, paint);
+ }
+ }
+private:
+ const Layout& layout;
+ Canvas* canvas;
+ float hOffset;
+ float vOffset;
+ const Paint& paint;
+ const SkPath& path;
+};
+
+static void drawTextOnPath(Canvas* canvas, const uint16_t* text, int count, int bidiFlags,
+ const SkPath& path, float hOffset, float vOffset,
+ const Paint& paint, TypefaceImpl* typeface) {
+ Paint paintCopy(paint);
+ Layout layout;
+ std::string css = MinikinUtils::setLayoutProperties(&layout, &paintCopy, bidiFlags, typeface);
+ layout.doLayout(text, 0, count, count, css);
+ hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
+
+ // Set align to left for drawing, as we don't want individual
+ // glyphs centered or right-aligned; the offset above takes
+ // care of all alignment.
+ paintCopy.setTextAlign(Paint::kLeft_Align);
+
+ DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paintCopy, path);
+ MinikinUtils::forFontRun(layout, &paintCopy, f);
+}
+
+static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
+ jint index, jint count, jlong pathHandle, jfloat hOffset,
+ jfloat vOffset, jint bidiFlags, jlong paintHandle,
+ jlong typefaceHandle) {
+ SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+ jchar* jchars = env->GetCharArrayElements(text, NULL);
+
+ drawTextOnPath(get_canvas(canvasHandle), jchars + index, count, bidiFlags, *path,
+ hOffset, vOffset, *paint, typeface);
+
+ env->ReleaseCharArrayElements(text, jchars, 0);
+}
+
+static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
+ jlong pathHandle, jfloat hOffset, jfloat vOffset,
+ jint bidiFlags, jlong paintHandle, jlong typefaceHandle) {
+ SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+ const jchar* jchars = env->GetStringChars(text, NULL);
+ int count = env->GetStringLength(text);
+
+ drawTextOnPath(get_canvas(canvasHandle), jchars, count, bidiFlags, *path,
+ hOffset, vOffset, *paint, typeface);
+
+ env->ReleaseStringChars(text, jchars);
+}
+
+static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, jlong filterHandle) {
+ get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
+}
+
+static void freeCaches(JNIEnv* env, jobject) {
+ SkGraphics::PurgeFontCache();
+}
+
+static void freeTextLayoutCaches(JNIEnv* env, jobject) {
+ Layout::purgeCaches();
+}
+
+}; // namespace CanvasJNI
+
+static JNINativeMethod gMethods[] = {
+ {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
+ {"initRaster", "(J)J", (void*) CanvasJNI::initRaster},
+ {"native_setBitmap", "(JJZ)V", (void*) CanvasJNI::setBitmap},
+ {"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
+ {"native_getWidth","(J)I", (void*) CanvasJNI::getWidth},
+ {"native_getHeight","(J)I", (void*) CanvasJNI::getHeight},
+ {"native_save","(JI)I", (void*) CanvasJNI::save},
+ {"native_saveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
+ {"native_saveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
+ {"native_getSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
+ {"native_restore","(J)V", (void*) CanvasJNI::restore},
+ {"native_restoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
+ {"native_getCTM", "(JJ)V", (void*)CanvasJNI::getCTM},
+ {"native_setMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
+ {"native_concat","(JJ)V", (void*) CanvasJNI::concat},
+ {"native_rotate","(JF)V", (void*) CanvasJNI::rotate},
+ {"native_scale","(JFF)V", (void*) CanvasJNI::scale},
+ {"native_skew","(JFF)V", (void*) CanvasJNI::skew},
+ {"native_translate","(JFF)V", (void*) CanvasJNI::translate},
+ {"native_getClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
+ {"native_quickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
+ {"native_quickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
+ {"native_clipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
+ {"native_clipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
+ {"native_clipRegion","(JJI)Z", (void*) CanvasJNI::clipRegion},
+ {"native_drawColor","(JII)V", (void*) CanvasJNI::drawColor},
+ {"native_drawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
+ {"native_drawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
+ {"native_drawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
+ {"native_drawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
+ {"native_drawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
+ {"native_drawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
+ {"native_drawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
+ {"native_drawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
+ {"native_drawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
+ {"native_drawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
+ {"native_drawPath","(JJJ)V", (void*) CanvasJNI::drawPath},
+ {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
+ {"native_drawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap},
+ {"nativeDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix},
+ {"native_drawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
+ {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
+ {"nativeDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
+ {"native_drawText","(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
+ {"native_drawText","(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
+ {"native_drawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
+ {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
+ {"native_drawTextOnPath","(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
+ {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
+ {"nativeSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter},
+ {"freeCaches", "()V", (void*) CanvasJNI::freeCaches},
+ {"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}
+};
+
+int register_android_graphics_Canvas(JNIEnv* env) {
+ return AndroidRuntime::registerNativeMethods(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/android_graphics_Picture.cpp b/core/jni/android_graphics_Picture.cpp
index f827907..eb8f6dd 100644
--- a/core/jni/android_graphics_Picture.cpp
+++ b/core/jni/android_graphics_Picture.cpp
@@ -51,7 +51,7 @@
static void android_graphics_Picture_draw(JNIEnv* env, jobject, jlong canvasHandle,
jlong pictureHandle) {
- SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
+ Canvas* canvas = reinterpret_cast<Canvas*>(canvasHandle);
Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
SkASSERT(canvas);
SkASSERT(picture);
@@ -84,12 +84,7 @@
static jlong android_graphics_Picture_beginRecording(JNIEnv* env, jobject, jlong pictHandle,
jint w, jint h) {
Picture* pict = reinterpret_cast<Picture*>(pictHandle);
- // beginRecording does not ref its return value, it just returns it.
- SkCanvas* canvas = pict->beginRecording(w, h);
- // the java side will wrap this guy in a Canvas.java, which will call
- // unref in its finalizer, so we have to ref it here, so that both that
- // Canvas.java and our picture can both be owners
- canvas->ref();
+ Canvas* canvas = pict->beginRecording(w, h);
return reinterpret_cast<jlong>(canvas);
}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 760ed45..32cf286 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -79,26 +79,6 @@
jmethodID setVendorInfo;
} dhcpResultsFieldIds;
-static jint android_net_utils_enableInterface(JNIEnv* env, jobject clazz, jstring ifname)
-{
- int result;
-
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- result = ::ifc_enable(nameStr);
- env->ReleaseStringUTFChars(ifname, nameStr);
- return (jint)result;
-}
-
-static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstring ifname)
-{
- int result;
-
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- result = ::ifc_disable(nameStr);
- env->ReleaseStringUTFChars(ifname, nameStr);
- return (jint)result;
-}
-
static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz,
jstring ifname, jint mask)
{
@@ -280,9 +260,6 @@
*/
static JNINativeMethod gNetworkUtilMethods[] = {
/* name, signature, funcPtr */
-
- { "enableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_enableInterface },
- { "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface },
{ "resetConnections", "(Ljava/lang/String;I)I", (void *)android_net_utils_resetConnections },
{ "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z", (void *)android_net_utils_runDhcp },
{ "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z", (void *)android_net_utils_runDhcpRenew },
diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp
deleted file mode 100644
index 28a8a5d..0000000
--- a/core/jni/android_text_format_Time.cpp
+++ /dev/null
@@ -1,689 +0,0 @@
-/*
-** Copyright 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.
-*/
-
-#define LOG_TAG "Log_println"
-
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <assert.h>
-
-#include "jni.h"
-#include "utils/misc.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "ScopedStringChars.h"
-#include "TimeUtils.h"
-#include <nativehelper/JNIHelp.h>
-#include <cutils/tztime.h>
-
-namespace android {
-
-static jfieldID g_allDayField = 0;
-static jfieldID g_secField = 0;
-static jfieldID g_minField = 0;
-static jfieldID g_hourField = 0;
-static jfieldID g_mdayField = 0;
-static jfieldID g_monField = 0;
-static jfieldID g_yearField = 0;
-static jfieldID g_wdayField = 0;
-static jfieldID g_ydayField = 0;
-static jfieldID g_isdstField = 0;
-static jfieldID g_gmtoffField = 0;
-static jfieldID g_timezoneField = 0;
-
-static jfieldID g_shortMonthsField = 0;
-static jfieldID g_longMonthsField = 0;
-static jfieldID g_longStandaloneMonthsField = 0;
-static jfieldID g_shortWeekdaysField = 0;
-static jfieldID g_longWeekdaysField = 0;
-static jfieldID g_timeOnlyFormatField = 0;
-static jfieldID g_dateOnlyFormatField = 0;
-static jfieldID g_dateTimeFormatField = 0;
-static jfieldID g_amField = 0;
-static jfieldID g_pmField = 0;
-static jfieldID g_dateCommandField = 0;
-static jfieldID g_localeField = 0;
-
-static jclass g_timeClass = NULL;
-
-static inline bool java2time(JNIEnv* env, Time* t, jobject o)
-{
- t->t.tm_sec = env->GetIntField(o, g_secField);
- t->t.tm_min = env->GetIntField(o, g_minField);
- t->t.tm_hour = env->GetIntField(o, g_hourField);
- t->t.tm_mday = env->GetIntField(o, g_mdayField);
- t->t.tm_mon = env->GetIntField(o, g_monField);
- t->t.tm_year = (env->GetIntField(o, g_yearField))-1900;
- t->t.tm_wday = env->GetIntField(o, g_wdayField);
- t->t.tm_yday = env->GetIntField(o, g_ydayField);
- t->t.tm_isdst = env->GetIntField(o, g_isdstField);
- t->t.tm_gmtoff = env->GetLongField(o, g_gmtoffField);
- bool allDay = env->GetBooleanField(o, g_allDayField);
- if (allDay &&
- ((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) {
- jniThrowException(env, "java/lang/IllegalArgumentException",
- "allDay is true but sec, min, hour are not 0.");
- return false;
- }
- return true;
-}
-
-static inline void time2java(JNIEnv* env, jobject o, const Time &t)
-{
- env->SetIntField(o, g_secField, t.t.tm_sec);
- env->SetIntField(o, g_minField, t.t.tm_min);
- env->SetIntField(o, g_hourField, t.t.tm_hour);
- env->SetIntField(o, g_mdayField, t.t.tm_mday);
- env->SetIntField(o, g_monField, t.t.tm_mon);
- env->SetIntField(o, g_yearField, t.t.tm_year+1900);
- env->SetIntField(o, g_wdayField, t.t.tm_wday);
- env->SetIntField(o, g_ydayField, t.t.tm_yday);
- env->SetIntField(o, g_isdstField, t.t.tm_isdst);
- env->SetLongField(o, g_gmtoffField, t.t.tm_gmtoff);
-}
-
-#define ACQUIRE_TIMEZONE(This, t) \
- jstring timezoneString_##This \
- = (jstring) env->GetObjectField(This, g_timezoneField); \
- t.timezone = env->GetStringUTFChars(timezoneString_##This, NULL);
-
-#define RELEASE_TIMEZONE(This, t) \
- env->ReleaseStringUTFChars(timezoneString_##This, t.timezone);
-
-
-// ============================================================================
-
-static jlong android_text_format_Time_normalize(JNIEnv* env, jobject This,
- jboolean ignoreDst)
-{
- Time t;
- if (!java2time(env, &t, This)) return 0L;
- ACQUIRE_TIMEZONE(This, t)
-
- int64_t result = t.toMillis(ignoreDst != 0);
-
- time2java(env, This, t);
- RELEASE_TIMEZONE(This, t)
-
- return static_cast<jlong>(result);
-}
-
-static void android_text_format_Time_switchTimezone(JNIEnv* env, jobject This,
- jstring timezoneObject)
-{
- Time t;
- if (!java2time(env, &t, This)) return;
- ACQUIRE_TIMEZONE(This, t)
-
- const char* timezone = env->GetStringUTFChars(timezoneObject, NULL);
-
- t.switchTimezone(timezone);
-
- time2java(env, This, t);
- env->ReleaseStringUTFChars(timezoneObject, timezone);
- RELEASE_TIMEZONE(This, t)
-
- // we do this here because there's no point in reallocating the string
- env->SetObjectField(This, g_timezoneField, timezoneObject);
-}
-
-static jint android_text_format_Time_compare(JNIEnv* env, jobject clazz,
- jobject aObject, jobject bObject)
-{
- Time a, b;
-
- if (!java2time(env, &a, aObject)) return 0;
- ACQUIRE_TIMEZONE(aObject, a)
-
- if (!java2time(env, &b, bObject)) return 0;
- ACQUIRE_TIMEZONE(bObject, b)
-
- int result = Time::compare(a, b);
-
- RELEASE_TIMEZONE(aObject, a)
- RELEASE_TIMEZONE(bObject, b)
-
- return static_cast<jint>(result);
-}
-
-static jstring android_text_format_Time_format2445(JNIEnv* env, jobject This)
-{
- Time t;
- if (!java2time(env, &t, This)) return env->NewStringUTF("");
- bool allDay = env->GetBooleanField(This, g_allDayField);
-
- if (!allDay) {
- ACQUIRE_TIMEZONE(This, t)
- bool inUtc = strcmp("UTC", t.timezone) == 0;
- short buf[16];
- t.format2445(buf, true);
- RELEASE_TIMEZONE(This, t)
- if (inUtc) {
- // The letter 'Z' is appended to the end so allow for one
- // more character in the buffer.
- return env->NewString((jchar*)buf, 16);
- } else {
- return env->NewString((jchar*)buf, 15);
- }
- } else {
- short buf[8];
- t.format2445(buf, false);
- return env->NewString((jchar*)buf, 8);
- }
-}
-
-static jstring android_text_format_Time_format(JNIEnv* env, jobject This,
- jstring formatObject)
-{
- // We only teardown and setup our 'locale' struct and other state
- // when the Java-side locale changed. This is safe to do here
- // without locking because we're always called from Java code
- // synchronized on the class instance.
- static jobject js_locale_previous = NULL;
- static struct strftime_locale locale;
- static jstring js_mon[12], js_month[12], js_wday[7], js_weekday[7];
- static jstring js_standalone_month[12];
- static jstring js_X_fmt, js_x_fmt, js_c_fmt, js_am, js_pm, js_date_fmt;
-
- Time t;
- if (!java2time(env, &t, This)) return env->NewStringUTF("");
-
- jclass timeClass = g_timeClass;
- jobject js_locale = (jobject) env->GetStaticObjectField(timeClass, g_localeField);
- if (js_locale_previous != js_locale) {
- if (js_locale_previous != NULL) {
- // Free the old one.
- for (int i = 0; i < 12; i++) {
- env->ReleaseStringUTFChars(js_mon[i], locale.mon[i]);
- env->ReleaseStringUTFChars(js_month[i], locale.month[i]);
- env->ReleaseStringUTFChars(js_standalone_month[i], locale.standalone_month[i]);
- env->DeleteGlobalRef(js_mon[i]);
- env->DeleteGlobalRef(js_month[i]);
- env->DeleteGlobalRef(js_standalone_month[i]);
- }
-
- for (int i = 0; i < 7; i++) {
- env->ReleaseStringUTFChars(js_wday[i], locale.wday[i]);
- env->ReleaseStringUTFChars(js_weekday[i], locale.weekday[i]);
- env->DeleteGlobalRef(js_wday[i]);
- env->DeleteGlobalRef(js_weekday[i]);
- }
-
- env->ReleaseStringUTFChars(js_X_fmt, locale.X_fmt);
- env->ReleaseStringUTFChars(js_x_fmt, locale.x_fmt);
- env->ReleaseStringUTFChars(js_c_fmt, locale.c_fmt);
- env->ReleaseStringUTFChars(js_am, locale.am);
- env->ReleaseStringUTFChars(js_pm, locale.pm);
- env->ReleaseStringUTFChars(js_date_fmt, locale.date_fmt);
- env->DeleteGlobalRef(js_X_fmt);
- env->DeleteGlobalRef(js_x_fmt);
- env->DeleteGlobalRef(js_c_fmt);
- env->DeleteGlobalRef(js_am);
- env->DeleteGlobalRef(js_pm);
- env->DeleteGlobalRef(js_date_fmt);
- }
- js_locale_previous = js_locale;
-
- jobjectArray ja;
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortMonthsField);
- for (int i = 0; i < 12; i++) {
- // Calendar.JANUARY == 0.
- js_mon[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
- locale.mon[i] = env->GetStringUTFChars(js_mon[i], NULL);
- }
-
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longMonthsField);
- for (int i = 0; i < 12; i++) {
- // Calendar.JANUARY == 0.
- js_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
- locale.month[i] = env->GetStringUTFChars(js_month[i], NULL);
- }
-
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longStandaloneMonthsField);
- for (int i = 0; i < 12; i++) {
- // Calendar.JANUARY == 0.
- js_standalone_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
- locale.standalone_month[i] = env->GetStringUTFChars(js_standalone_month[i], NULL);
- }
-
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortWeekdaysField);
- for (int i = 0; i < 7; i++) {
- // Calendar.SUNDAY == 1, and there's an empty string in element 0.
- js_wday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i + 1));
- locale.wday[i] = env->GetStringUTFChars(js_wday[i], NULL);
- }
-
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longWeekdaysField);
- for (int i = 0; i < 7; i++) {
- // Calendar.SUNDAY == 1, and there's an empty string in element 0.
- js_weekday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i + 1));
- locale.weekday[i] = env->GetStringUTFChars(js_weekday[i], NULL);
- }
-
- js_X_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
- timeClass, g_timeOnlyFormatField));
- locale.X_fmt = env->GetStringUTFChars(js_X_fmt, NULL);
-
- js_x_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
- timeClass, g_dateOnlyFormatField));
- locale.x_fmt = env->GetStringUTFChars(js_x_fmt, NULL);
-
- js_c_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
- timeClass, g_dateTimeFormatField));
- locale.c_fmt = env->GetStringUTFChars(js_c_fmt, NULL);
-
- js_am = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
- timeClass, g_amField));
- locale.am = env->GetStringUTFChars(js_am, NULL);
-
- js_pm = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
- timeClass, g_pmField));
- locale.pm = env->GetStringUTFChars(js_pm, NULL);
-
- js_date_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
- timeClass, g_dateCommandField));
- locale.date_fmt = env->GetStringUTFChars(js_date_fmt, NULL);
- }
-
- ACQUIRE_TIMEZONE(This, t)
-
- const char* format = env->GetStringUTFChars(formatObject, NULL);
-
- String8 r = t.format(format, &locale);
-
- env->ReleaseStringUTFChars(formatObject, format);
- RELEASE_TIMEZONE(This, t)
-
- return env->NewStringUTF(r.string());
-}
-
-
-static jstring android_text_format_Time_toString(JNIEnv* env, jobject This)
-{
- Time t;
- if (!java2time(env, &t, This)) return env->NewStringUTF("");
- ACQUIRE_TIMEZONE(This, t)
-
- String8 r = t.toString();
-
- RELEASE_TIMEZONE(This, t)
-
- return env->NewStringUTF(r.string());
-}
-
-static void android_text_format_Time_setToNow(JNIEnv* env, jobject This)
-{
- env->SetBooleanField(This, g_allDayField, JNI_FALSE);
- Time t;
- ACQUIRE_TIMEZONE(This, t)
-
- t.setToNow();
-
- time2java(env, This, t);
- RELEASE_TIMEZONE(This, t)
-}
-
-static jlong android_text_format_Time_toMillis(JNIEnv* env, jobject This,
- jboolean ignoreDst)
-{
- Time t;
- if (!java2time(env, &t, This)) return 0L;
- ACQUIRE_TIMEZONE(This, t)
-
- int64_t result = t.toMillis(ignoreDst != 0);
-
- RELEASE_TIMEZONE(This, t)
-
- return static_cast<jlong>(result);
-}
-
-static void android_text_format_Time_set(JNIEnv* env, jobject This, jlong millis)
-{
- env->SetBooleanField(This, g_allDayField, JNI_FALSE);
- Time t;
- ACQUIRE_TIMEZONE(This, t)
-
- t.set(millis);
-
- time2java(env, This, t);
- RELEASE_TIMEZONE(This, t)
-}
-
-
-// ============================================================================
-// Just do this here because it's not worth recreating the strings
-
-static int get_char(JNIEnv* env, const ScopedStringChars& s, int spos, int mul,
- bool* thrown)
-{
- jchar c = s[spos];
- if (c >= '0' && c <= '9') {
- return (c - '0') * mul;
- } else {
- if (!*thrown) {
- jniThrowExceptionFmt(env, "android/util/TimeFormatException",
- "Parse error at pos=%d", spos);
- *thrown = true;
- }
- return 0;
- }
-}
-
-static bool check_char(JNIEnv* env, const ScopedStringChars& s, int spos, jchar expected)
-{
- jchar c = s[spos];
- if (c != expected) {
- jniThrowExceptionFmt(env, "android/util/TimeFormatException",
- "Unexpected character 0x%02x at pos=%d. Expected %c.",
- c, spos, expected);
- return false;
- }
- return true;
-}
-
-
-static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstring strObj)
-{
- jsize len = env->GetStringLength(strObj);
- if (len < 8) {
- jniThrowException(env, "android/util/TimeFormatException",
- "String too short -- expected at least 8 characters.");
- return JNI_FALSE;
- }
-
- jboolean inUtc = JNI_FALSE;
-
- ScopedStringChars s(env, strObj);
-
- // year
- int n;
- bool thrown = false;
- n = get_char(env, s, 0, 1000, &thrown);
- n += get_char(env, s, 1, 100, &thrown);
- n += get_char(env, s, 2, 10, &thrown);
- n += get_char(env, s, 3, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_yearField, n);
-
- // month
- n = get_char(env, s, 4, 10, &thrown);
- n += get_char(env, s, 5, 1, &thrown);
- n--;
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_monField, n);
-
- // day of month
- n = get_char(env, s, 6, 10, &thrown);
- n += get_char(env, s, 7, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_mdayField, n);
-
- if (len > 8) {
- // T
- if (!check_char(env, s, 8, 'T')) return JNI_FALSE;
- env->SetBooleanField(This, g_allDayField, JNI_FALSE);
-
- // hour
- n = get_char(env, s, 9, 10, &thrown);
- n += get_char(env, s, 10, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_hourField, n);
-
- // min
- n = get_char(env, s, 11, 10, &thrown);
- n += get_char(env, s, 12, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_minField, n);
-
- // sec
- n = get_char(env, s, 13, 10, &thrown);
- n += get_char(env, s, 14, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_secField, n);
-
- if (len > 15) {
- // Z
- if (!check_char(env, s, 15, 'Z')) return JNI_FALSE;
- inUtc = JNI_TRUE;
- }
- } else {
- env->SetBooleanField(This, g_allDayField, JNI_TRUE);
- env->SetIntField(This, g_hourField, 0);
- env->SetIntField(This, g_minField, 0);
- env->SetIntField(This, g_secField, 0);
- }
-
- env->SetIntField(This, g_wdayField, 0);
- env->SetIntField(This, g_ydayField, 0);
- env->SetIntField(This, g_isdstField, -1);
- env->SetLongField(This, g_gmtoffField, 0);
-
- return inUtc;
-}
-
-static jboolean android_text_format_Time_parse3339(JNIEnv* env,
- jobject This,
- jstring strObj)
-{
- jsize len = env->GetStringLength(strObj);
- if (len < 10) {
- jniThrowException(env, "android/util/TimeFormatException",
- "String too short --- expected at least 10 characters.");
- return JNI_FALSE;
- }
-
- jboolean inUtc = JNI_FALSE;
-
- ScopedStringChars s(env, strObj);
-
- // year
- int n;
- bool thrown = false;
- n = get_char(env, s, 0, 1000, &thrown);
- n += get_char(env, s, 1, 100, &thrown);
- n += get_char(env, s, 2, 10, &thrown);
- n += get_char(env, s, 3, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_yearField, n);
-
- // -
- if (!check_char(env, s, 4, '-')) return JNI_FALSE;
-
- // month
- n = get_char(env, s, 5, 10, &thrown);
- n += get_char(env, s, 6, 1, &thrown);
- --n;
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_monField, n);
-
- // -
- if (!check_char(env, s, 7, '-')) return JNI_FALSE;
-
- // day
- n = get_char(env, s, 8, 10, &thrown);
- n += get_char(env, s, 9, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_mdayField, n);
-
- if (len >= 19) {
- // T
- if (!check_char(env, s, 10, 'T')) return JNI_FALSE;
-
- env->SetBooleanField(This, g_allDayField, JNI_FALSE);
- // hour
- n = get_char(env, s, 11, 10, &thrown);
- n += get_char(env, s, 12, 1, &thrown);
- if (thrown) return JNI_FALSE;
- int hour = n;
- // env->SetIntField(This, g_hourField, n);
-
- // :
- if (!check_char(env, s, 13, ':')) return JNI_FALSE;
-
- // minute
- n = get_char(env, s, 14, 10, &thrown);
- n += get_char(env, s, 15, 1, &thrown);
- if (thrown) return JNI_FALSE;
- int minute = n;
- // env->SetIntField(This, g_minField, n);
-
- // :
- if (!check_char(env, s, 16, ':')) return JNI_FALSE;
-
- // second
- n = get_char(env, s, 17, 10, &thrown);
- n += get_char(env, s, 18, 1, &thrown);
- if (thrown) return JNI_FALSE;
- env->SetIntField(This, g_secField, n);
-
- // skip the '.XYZ' -- we don't care about subsecond precision.
- int tz_index = 19;
- if (tz_index < len && s[tz_index] == '.') {
- do {
- tz_index++;
- } while (tz_index < len
- && s[tz_index] >= '0'
- && s[tz_index] <= '9');
- }
-
- int offset = 0;
- if (len > tz_index) {
- char c = s[tz_index];
-
- // NOTE: the offset is meant to be subtracted to get from local time
- // to UTC. we therefore use 1 for '-' and -1 for '+'.
- switch (c) {
- case 'Z':
- // Zulu time -- UTC
- offset = 0;
- break;
- case '-':
- offset = 1;
- break;
- case '+':
- offset = -1;
- break;
- default:
- jniThrowExceptionFmt(env, "android/util/TimeFormatException",
- "Unexpected character 0x%02x at position %d. Expected + or -",
- c, tz_index);
- return JNI_FALSE;
- }
- inUtc = JNI_TRUE;
-
- if (offset != 0) {
- if (len < tz_index + 6) {
- jniThrowExceptionFmt(env, "android/util/TimeFormatException",
- "Unexpected length; should be %d characters",
- tz_index + 6);
- return JNI_FALSE;
- }
-
- // hour
- n = get_char(env, s, tz_index + 1, 10, &thrown);
- n += get_char(env, s, tz_index + 2, 1, &thrown);
- if (thrown) return JNI_FALSE;
- n *= offset;
- hour += n;
-
- // :
- if (!check_char(env, s, tz_index + 3, ':')) return JNI_FALSE;
-
- // minute
- n = get_char(env, s, tz_index + 4, 10, &thrown);
- n += get_char(env, s, tz_index + 5, 1, &thrown);
- if (thrown) return JNI_FALSE;
- n *= offset;
- minute += n;
- }
- }
- env->SetIntField(This, g_hourField, hour);
- env->SetIntField(This, g_minField, minute);
-
- if (offset != 0) {
- // we need to normalize after applying the hour and minute offsets
- android_text_format_Time_normalize(env, This, false /* use isdst */);
- // The timezone is set to UTC in the calling Java code.
- }
- } else {
- env->SetBooleanField(This, g_allDayField, JNI_TRUE);
- env->SetIntField(This, g_hourField, 0);
- env->SetIntField(This, g_minField, 0);
- env->SetIntField(This, g_secField, 0);
- }
-
- env->SetIntField(This, g_wdayField, 0);
- env->SetIntField(This, g_ydayField, 0);
- env->SetIntField(This, g_isdstField, -1);
- env->SetLongField(This, g_gmtoffField, 0);
-
- return inUtc;
-}
-
-// ============================================================================
-/*
- * JNI registration.
- */
-static JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "normalize", "(Z)J", (void*)android_text_format_Time_normalize },
- { "switchTimezone", "(Ljava/lang/String;)V", (void*)android_text_format_Time_switchTimezone },
- { "nativeCompare", "(Landroid/text/format/Time;Landroid/text/format/Time;)I", (void*)android_text_format_Time_compare },
- { "format1", "(Ljava/lang/String;)Ljava/lang/String;", (void*)android_text_format_Time_format },
- { "format2445", "()Ljava/lang/String;", (void*)android_text_format_Time_format2445 },
- { "toString", "()Ljava/lang/String;", (void*)android_text_format_Time_toString },
- { "nativeParse", "(Ljava/lang/String;)Z", (void*)android_text_format_Time_parse },
- { "nativeParse3339", "(Ljava/lang/String;)Z", (void*)android_text_format_Time_parse3339 },
- { "setToNow", "()V", (void*)android_text_format_Time_setToNow },
- { "toMillis", "(Z)J", (void*)android_text_format_Time_toMillis },
- { "set", "(J)V", (void*)android_text_format_Time_set }
-};
-
-int register_android_text_format_Time(JNIEnv* env)
-{
- jclass timeClass = env->FindClass("android/text/format/Time");
-
- g_timeClass = (jclass) env->NewGlobalRef(timeClass);
-
- g_allDayField = env->GetFieldID(timeClass, "allDay", "Z");
- g_secField = env->GetFieldID(timeClass, "second", "I");
- g_minField = env->GetFieldID(timeClass, "minute", "I");
- g_hourField = env->GetFieldID(timeClass, "hour", "I");
- g_mdayField = env->GetFieldID(timeClass, "monthDay", "I");
- g_monField = env->GetFieldID(timeClass, "month", "I");
- g_yearField = env->GetFieldID(timeClass, "year", "I");
- g_wdayField = env->GetFieldID(timeClass, "weekDay", "I");
- g_ydayField = env->GetFieldID(timeClass, "yearDay", "I");
- g_isdstField = env->GetFieldID(timeClass, "isDst", "I");
- g_gmtoffField = env->GetFieldID(timeClass, "gmtoff", "J");
- g_timezoneField = env->GetFieldID(timeClass, "timezone", "Ljava/lang/String;");
-
- g_shortMonthsField = env->GetStaticFieldID(timeClass, "sShortMonths", "[Ljava/lang/String;");
- g_longMonthsField = env->GetStaticFieldID(timeClass, "sLongMonths", "[Ljava/lang/String;");
- g_longStandaloneMonthsField = env->GetStaticFieldID(timeClass, "sLongStandaloneMonths", "[Ljava/lang/String;");
- g_shortWeekdaysField = env->GetStaticFieldID(timeClass, "sShortWeekdays", "[Ljava/lang/String;");
- g_longWeekdaysField = env->GetStaticFieldID(timeClass, "sLongWeekdays", "[Ljava/lang/String;");
- g_timeOnlyFormatField = env->GetStaticFieldID(timeClass, "sTimeOnlyFormat", "Ljava/lang/String;");
- g_dateOnlyFormatField = env->GetStaticFieldID(timeClass, "sDateOnlyFormat", "Ljava/lang/String;");
- g_dateTimeFormatField = env->GetStaticFieldID(timeClass, "sDateTimeFormat", "Ljava/lang/String;");
- g_amField = env->GetStaticFieldID(timeClass, "sAm", "Ljava/lang/String;");
- g_pmField = env->GetStaticFieldID(timeClass, "sPm", "Ljava/lang/String;");
- g_dateCommandField = env->GetStaticFieldID(timeClass, "sDateCommand", "Ljava/lang/String;");
- g_localeField = env->GetStaticFieldID(timeClass, "sLocale", "Ljava/util/Locale;");
-
- return AndroidRuntime::registerNativeMethods(env, "android/text/format/Time", gMethods, NELEM(gMethods));
-}
-
-}; // namespace android
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 8641e73..0a259aa 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -32,7 +32,6 @@
#include <SkCanvas.h>
#include <SkImageInfo.h>
#include <SkMatrix.h>
-#include <SkPaint.h>
#include <SkPorterDuff.h>
#include <SkRegion.h>
#include <SkScalerContext.h>
@@ -43,6 +42,7 @@
#include <Rect.h>
#include <RenderNode.h>
#include <CanvasProperty.h>
+#include <Paint.h>
#include <minikin/Layout.h>
#include "MinikinSkia.h"
@@ -203,14 +203,14 @@
jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
jlong paintPtr, jint saveFlags) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
return renderer->saveLayer(left, top, right, bottom, paint, saveFlags);
}
static jint android_view_GLES20Canvas_saveLayerClip(JNIEnv* env, jobject clazz,
jlong rendererPtr, jlong paintPtr, jint saveFlags) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
const android::uirenderer::Rect& bounds(renderer->getLocalClipBounds());
return renderer->saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom,
paint, saveFlags);
@@ -349,7 +349,7 @@
JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawBitmap(bitmap, left, top, paint);
}
@@ -362,7 +362,7 @@
JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
dstLeft, dstTop, dstRight, dstBottom, paint);
}
@@ -376,7 +376,7 @@
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawBitmap(bitmap, *matrix, paint);
}
@@ -400,7 +400,7 @@
}
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawBitmapData(bitmap, left, top, paint);
// Note - bitmap isn't deleted as DisplayListRenderer owns it now
@@ -418,7 +418,7 @@
jint* colorsArray = colors ? env->GetIntArrayElements(colors, NULL) + colorOffset : NULL;
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawBitmapMesh(bitmap, meshWidth, meshHeight, verticesArray, colorsArray, paint);
if (vertices) env->ReleaseFloatArrayElements(vertices, verticesArray, 0);
@@ -434,7 +434,7 @@
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(patchPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint);
}
@@ -449,7 +449,7 @@
jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
jlong paintPtr) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawRect(left, top, right, bottom, paint);
}
@@ -457,14 +457,14 @@
jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
jfloat rx, jfloat ry, jlong paintPtr) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawRoundRect(left, top, right, bottom, rx, ry, paint);
}
static void android_view_GLES20Canvas_drawCircle(JNIEnv* env, jobject clazz,
jlong rendererPtr, jfloat x, jfloat y, jfloat radius, jlong paintPtr) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawCircle(x, y, radius, paint);
}
@@ -482,7 +482,7 @@
jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
jlong paintPtr) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawOval(left, top, right, bottom, paint);
}
@@ -490,7 +490,7 @@
jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
jfloat startAngle, jfloat sweepAngle, jboolean useCenter, jlong paintPtr) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawArc(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint);
}
@@ -498,8 +498,8 @@
jlong rendererPtr, jlong regionPtr, jlong paintPtr) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
SkRegion* region = reinterpret_cast<SkRegion*>(regionPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
- if (paint->getStyle() != SkPaint::kFill_Style ||
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
+ if (paint->getStyle() != Paint::kFill_Style ||
(paint->isAntiAlias() && !renderer->isCurrentTransformSimple())) {
SkRegion::Iterator it(*region);
while (!it.done()) {
@@ -528,7 +528,7 @@
jlong rendererPtr, jfloatArray points, jint offset, jint count, jlong paintPtr) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
jfloat* storage = env->GetFloatArrayElements(points, NULL);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawPoints(storage + offset, count, paint);
env->ReleaseFloatArrayElements(points, storage, 0);
}
@@ -537,7 +537,7 @@
jlong rendererPtr, jlong pathPtr, jlong paintPtr) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawPath(path, paint);
}
@@ -545,7 +545,7 @@
jlong rendererPtr, jfloatArray points, jint offset, jint count, jlong paintPtr) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
jfloat* storage = env->GetFloatArrayElements(points, NULL);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
renderer->drawLines(storage + offset, count, paint);
env->ReleaseFloatArrayElements(points, storage, 0);
}
@@ -573,7 +573,7 @@
class RenderTextFunctor {
public:
RenderTextFunctor(const Layout& layout, DisplayListRenderer* renderer, jfloat x, jfloat y,
- SkPaint* paint, uint16_t* glyphs, float* pos, float totalAdvance,
+ Paint* paint, uint16_t* glyphs, float* pos, float totalAdvance,
uirenderer::Rect& bounds)
: layout(layout), renderer(renderer), x(x), y(y), paint(paint), glyphs(glyphs),
pos(pos), totalAdvance(totalAdvance), bounds(bounds) { }
@@ -593,7 +593,7 @@
DisplayListRenderer* renderer;
jfloat x;
jfloat y;
- SkPaint* paint;
+ Paint* paint;
uint16_t* glyphs;
float* pos;
float totalAdvance;
@@ -601,7 +601,7 @@
};
static void renderTextLayout(DisplayListRenderer* renderer, Layout* layout,
- jfloat x, jfloat y, SkPaint* paint) {
+ jfloat x, jfloat y, Paint* paint) {
size_t nGlyphs = layout->nGlyphs();
float* pos = new float[nGlyphs * 2];
uint16_t* glyphs = new uint16_t[nGlyphs];
@@ -618,7 +618,7 @@
}
static void renderText(DisplayListRenderer* renderer, const jchar* text, int count,
- jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
+ jfloat x, jfloat y, int bidiFlags, Paint* paint, TypefaceImpl* typeface) {
Layout layout;
std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
layout.doLayout(text, 0, count, count, css);
@@ -629,7 +629,7 @@
class RenderTextOnPathFunctor {
public:
RenderTextOnPathFunctor(const Layout& layout, DisplayListRenderer* renderer, float hOffset,
- float vOffset, SkPaint* paint, SkPath* path)
+ float vOffset, Paint* paint, SkPath* path)
: layout(layout), renderer(renderer), hOffset(hOffset), vOffset(vOffset),
paint(paint), path(path) {
}
@@ -647,19 +647,19 @@
DisplayListRenderer* renderer;
float hOffset;
float vOffset;
- SkPaint* paint;
+ Paint* paint;
SkPath* path;
};
static void renderTextOnPath(DisplayListRenderer* renderer, const jchar* text, int count,
- SkPath* path, jfloat hOffset, jfloat vOffset, int bidiFlags, SkPaint* paint,
+ SkPath* path, jfloat hOffset, jfloat vOffset, int bidiFlags, Paint* paint,
TypefaceImpl* typeface) {
Layout layout;
std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
layout.doLayout(text, 0, count, count, css);
hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path);
- SkPaint::Align align = paint->getTextAlign();
- paint->setTextAlign(SkPaint::kLeft_Align);
+ Paint::Align align = paint->getTextAlign();
+ paint->setTextAlign(Paint::kLeft_Align);
RenderTextOnPathFunctor f(layout, renderer, hOffset, vOffset, paint, path);
MinikinUtils::forFontRun(layout, paint, f);
@@ -668,7 +668,7 @@
static void renderTextRun(DisplayListRenderer* renderer, const jchar* text,
jint start, jint count, jint contextCount, jfloat x, jfloat y,
- int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
+ int bidiFlags, Paint* paint, TypefaceImpl* typeface) {
Layout layout;
std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
layout.doLayout(text, start, count, contextCount, css);
@@ -681,7 +681,7 @@
jfloat x, jfloat y, jint bidiFlags, jlong paintPtr, jlong typefacePtr) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
jchar* textArray = env->GetCharArrayElements(text, NULL);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
renderText(renderer, textArray + index, count, x, y, bidiFlags, paint, typeface);
@@ -693,7 +693,7 @@
jfloat x, jfloat y, jint bidiFlags, jlong paintPtr, jlong typefacePtr) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
const jchar* textArray = env->GetStringChars(text, NULL);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
renderText(renderer, textArray + start, end - start, x, y, bidiFlags, paint, typeface);
@@ -707,7 +707,7 @@
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
jchar* textArray = env->GetCharArrayElements(text, NULL);
SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
renderTextOnPath(renderer, textArray + index, count, path,
@@ -722,7 +722,7 @@
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
const jchar* textArray = env->GetStringChars(text, NULL);
SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
renderTextOnPath(renderer, textArray + start, end - start, path,
@@ -736,7 +736,7 @@
jlong paintPtr, jlong typefacePtr) {
DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
jchar* textArray = env->GetCharArrayElements(text, NULL);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
@@ -753,7 +753,7 @@
const jchar* textArray = env->GetStringChars(text, NULL);
jint count = end - start;
jint contextCount = contextEnd - contextStart;
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index 50edb76..aa674de 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -25,10 +25,10 @@
#include <gui/GLConsumer.h>
+#include <Paint.h>
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkMatrix.h>
-#include <SkPaint.h>
#include <SkXfermode.h>
#include <DeferredLayerUpdater.h>
@@ -56,7 +56,7 @@
jlong layerUpdaterPtr, jlong paintPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
if (layer) {
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
layer->setPaint(paint);
}
}
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 28473e0..1f3909a 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -26,6 +26,7 @@
#include <Animator.h>
#include <DisplayListRenderer.h>
#include <RenderNode.h>
+#include <Paint.h>
namespace android {
@@ -95,7 +96,7 @@
static jboolean android_view_RenderNode_setLayerPaint(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jlong paintPtr) {
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
return SET_AND_DIRTY(mutateLayerProperties().setFromPaint, paint, RenderNode::GENERIC);
}
@@ -152,6 +153,14 @@
return true;
}
+static jboolean android_view_RenderNode_setOutlineNone(JNIEnv* env,
+ jobject clazz, jlong renderNodePtr) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ renderNode->mutateStagingProperties().mutableOutline().setNone();
+ renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return true;
+}
+
static jboolean android_view_RenderNode_setClipToOutline(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jboolean clipToOutline) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
@@ -485,6 +494,7 @@
{ "nSetOutlineRoundRect", "(JIIIIF)Z", (void*) android_view_RenderNode_setOutlineRoundRect },
{ "nSetOutlineConvexPath", "(JJ)Z", (void*) android_view_RenderNode_setOutlineConvexPath },
{ "nSetOutlineEmpty", "(J)Z", (void*) android_view_RenderNode_setOutlineEmpty },
+ { "nSetOutlineNone", "(J)Z", (void*) android_view_RenderNode_setOutlineNone },
{ "nSetClipToOutline", "(JZ)Z", (void*) android_view_RenderNode_setClipToOutline },
{ "nSetRevealClip", "(JZZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 988d461..d183d8e 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -219,9 +219,11 @@
static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr,
jint width, jint height,
- jfloat lightX, jfloat lightY, jfloat lightZ, jfloat lightRadius) {
+ jfloat lightX, jfloat lightY, jfloat lightZ, jfloat lightRadius,
+ jint ambientShadowAlpha, jint spotShadowAlpha) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
- proxy->setup(width, height, Vector3(lightX, lightY, lightZ), lightRadius);
+ proxy->setup(width, height, (Vector3){lightX, lightY, lightZ}, lightRadius,
+ ambientShadowAlpha, spotShadowAlpha);
}
static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz,
@@ -358,7 +360,7 @@
{ "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize },
{ "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
{ "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
- { "nSetup", "(JIIFFFF)V", (void*) android_view_ThreadedRenderer_setup },
+ { "nSetup", "(JIIFFFFII)V", (void*) android_view_ThreadedRenderer_setup },
{ "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
{ "nSyncAndDrawFrame", "(JJJF)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
{ "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fc55116..056d470 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -894,7 +894,7 @@
android:label="@string/permlab_nfc" />
<!-- @SystemApi Allows an internal user to use privileged ConnectivityManager APIs.
- @hide -->
+ @hide -->
<permission android:name="android.permission.CONNECTIVITY_INTERNAL"
android:permissionGroup="android.permission-group.NETWORK"
android:protectionLevel="signature|system" />
@@ -905,10 +905,10 @@
android:protectionLevel="signature|system" />
<!-- @SystemApi Allows access to the loop radio (Android@Home mesh network) device.
- @hide -->
+ @hide -->
<permission android:name="android.permission.LOOP_RADIO"
- android:permissionGroup="android.permission-group.NETWORK"
- android:protectionLevel="signature|system" />
+ android:permissionGroup="android.permission-group.NETWORK"
+ android:protectionLevel="signature|system" />
<!-- Allows sending and receiving handover transfer status from Wifi and Bluetooth
@hide
@@ -1095,6 +1095,13 @@
<permission android:name="android.permission.TV_INPUT_HARDWARE"
android:protectionLevel="signatureOrSystem" />
+ <!-- @SystemApi Allows to capture a frame of TV input hardware such as
+ built-in tuners and HDMI-in's.
+ @hide <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.CAPTURE_TV_INPUT"
+ android:protectionLevel="signatureOrSystem" />
+
<!-- @hide Allows enabling/disabling OEM unlock
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.OEM_UNLOCK_STATE"
@@ -1364,6 +1371,14 @@
android:label="@string/permlab_getTasks"
android:description="@string/permdesc_getTasks" />
+ <!-- Allows an application to start a task from a ActivityManager#RecentTaskInfo.
+ @hide -->
+ <permission android:name="android.permission.START_TASKS_FROM_RECENTS"
+ android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+ android:protectionLevel="signature|system"
+ android:label="@string/permlab_startTasksFromRecents"
+ android:description="@string/permdesc_startTasksFromRecents" />
+
<!-- @SystemApi @hide Allows an application to call APIs that allow it to do interactions
across the users on the device, using singleton services and
user-targeted broadcasts. This permission is not available to
@@ -2461,14 +2476,15 @@
<permission android:name="android.permission.PACKAGE_USAGE_STATS"
android:label="@string/permlab_pkgUsageStats"
android:description="@string/permdesc_pkgUsageStats"
- android:protectionLevel="signature|system" />
+ android:protectionLevel="signature|system|development|appop" />
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<!-- @SystemApi Allows an application to collect battery statistics -->
<permission android:name="android.permission.BATTERY_STATS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:label="@string/permlab_batteryStats"
android:description="@string/permdesc_batteryStats"
- android:protectionLevel="signature|system" />
+ android:protectionLevel="signature|system|development" />
<!-- @SystemApi Allows an application to control the backup and restore process.
<p>Not for use by third-party applications.
@@ -2796,12 +2812,12 @@
android:icon="@drawable/ic_launcher_android"
android:supportsRtl="true">
<activity android:name="com.android.internal.app.ChooserActivity"
- android:theme="@style/Theme.Holo.Dialog.Alert"
+ android:theme="@style/Theme.DeviceDefault.Resolver"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
- android:multiprocess="true"
android:documentLaunchMode="never"
- android:relinquishTaskIdentity="true">
+ android:relinquishTaskIdentity="true"
+ android:process=":ui">
<intent-filter>
<action android:name="android.intent.action.CHOOSER" />
<category android:name="android.intent.category.DEFAULT" />
@@ -2847,7 +2863,7 @@
<activity android:name="android.accounts.ChooseAccountActivity"
android:excludeFromRecents="true"
android:exported="true"
- android:theme="@android:style/Theme.Holo.Dialog"
+ android:theme="@android:style/Theme.Material.Dialog"
android:label="@string/choose_account_label"
android:process=":ui">
</activity>
@@ -2855,14 +2871,14 @@
<activity android:name="android.accounts.ChooseTypeAndAccountActivity"
android:excludeFromRecents="true"
android:exported="true"
- android:theme="@android:style/Theme.Holo.Dialog"
+ android:theme="@android:style/Theme.Material.Dialog"
android:label="@string/choose_account_label"
android:process=":ui">
</activity>
<activity android:name="android.accounts.ChooseAccountTypeActivity"
android:excludeFromRecents="true"
- android:theme="@android:style/Theme.Holo.Dialog"
+ android:theme="@android:style/Theme.Material.Dialog"
android:label="@string/choose_account_label"
android:process=":ui">
</activity>
@@ -2870,14 +2886,14 @@
<activity android:name="android.accounts.CantAddAccountActivity"
android:excludeFromRecents="true"
android:exported="true"
- android:theme="@android:style/Theme.Holo.Dialog.NoActionBar"
+ android:theme="@android:style/Theme.Material.Dialog.NoActionBar"
android:process=":ui">
</activity>
<activity android:name="android.accounts.GrantCredentialsPermissionActivity"
android:excludeFromRecents="true"
android:exported="true"
- android:theme="@android:style/Theme.Holo.DialogWhenLarge"
+ android:theme="@android:style/Theme.Material.DialogWhenLarge"
android:process=":ui">
</activity>
diff --git a/core/res/res/drawable-hdpi/ic_ab_back_mtrl_am_alpha.png b/core/res/res/drawable-hdpi/ic_ab_back_mtrl_am_alpha.png
index f0910d8..6c36eae 100644
--- a/core/res/res/drawable-hdpi/ic_ab_back_mtrl_am_alpha.png
+++ b/core/res/res/drawable-hdpi/ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_search_mtrl_alpha.png b/core/res/res/drawable-hdpi/ic_menu_search_mtrl_alpha.png
index a0501b3..f7382d3 100644
--- a/core/res/res/drawable-hdpi/ic_menu_search_mtrl_alpha.png
+++ b/core/res/res/drawable-hdpi/ic_menu_search_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_search_api_mtrl_alpha.png b/core/res/res/drawable-hdpi/ic_search_api_mtrl_alpha.png
index cac32b5..f7382d3 100644
--- a/core/res/res/drawable-hdpi/ic_search_api_mtrl_alpha.png
+++ b/core/res/res/drawable-hdpi/ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_mtrl_am_alpha.9.png b/core/res/res/drawable-hdpi/spinner_mtrl_am_alpha.9.png
index de5819d..de7ac29 100644
--- a/core/res/res/drawable-hdpi/spinner_mtrl_am_alpha.9.png
+++ b/core/res/res/drawable-hdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_activated_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/textfield_activated_mtrl_alpha.9.png
index 7c77a45..b9a81be 100644
--- a/core/res/res/drawable-hdpi/textfield_activated_mtrl_alpha.9.png
+++ b/core/res/res/drawable-hdpi/textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/textfield_default_mtrl_alpha.9.png
index bc6da21..3682629 100644
--- a/core/res/res/drawable-hdpi/textfield_default_mtrl_alpha.9.png
+++ b/core/res/res/drawable-hdpi/textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_ab_back_mtrl_am_alpha.png b/core/res/res/drawable-mdpi/ic_ab_back_mtrl_am_alpha.png
index e196bbe..6674351 100644
--- a/core/res/res/drawable-mdpi/ic_ab_back_mtrl_am_alpha.png
+++ b/core/res/res/drawable-mdpi/ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_search_mtrl_alpha.png b/core/res/res/drawable-mdpi/ic_menu_search_mtrl_alpha.png
index c5de768..0fb57b2 100644
--- a/core/res/res/drawable-mdpi/ic_menu_search_mtrl_alpha.png
+++ b/core/res/res/drawable-mdpi/ic_menu_search_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_search_api_mtrl_alpha.png b/core/res/res/drawable-mdpi/ic_search_api_mtrl_alpha.png
index 9137fea..0fb57b2 100644
--- a/core/res/res/drawable-mdpi/ic_search_api_mtrl_alpha.png
+++ b/core/res/res/drawable-mdpi/ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_mtrl_am_alpha.9.png b/core/res/res/drawable-mdpi/spinner_mtrl_am_alpha.9.png
index f151bfb..bbf5928 100644
--- a/core/res/res/drawable-mdpi/spinner_mtrl_am_alpha.9.png
+++ b/core/res/res/drawable-mdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_activated_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/textfield_activated_mtrl_alpha.9.png
index 7a72295..f3d06fe 100644
--- a/core/res/res/drawable-mdpi/textfield_activated_mtrl_alpha.9.png
+++ b/core/res/res/drawable-mdpi/textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/textfield_default_mtrl_alpha.9.png
index 483931f..f0e7db8 100644
--- a/core/res/res/drawable-mdpi/textfield_default_mtrl_alpha.9.png
+++ b/core/res/res/drawable-mdpi/textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_ab_back_mtrl_am_alpha.png b/core/res/res/drawable-xhdpi/ic_ab_back_mtrl_am_alpha.png
index 4385b2b..27bdcb7 100644
--- a/core/res/res/drawable-xhdpi/ic_ab_back_mtrl_am_alpha.png
+++ b/core/res/res/drawable-xhdpi/ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_search_mtrl_alpha.png b/core/res/res/drawable-xhdpi/ic_menu_search_mtrl_alpha.png
index 4602b35..05cfab7 100644
--- a/core/res/res/drawable-xhdpi/ic_menu_search_mtrl_alpha.png
+++ b/core/res/res/drawable-xhdpi/ic_menu_search_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_search_api_mtrl_alpha.png b/core/res/res/drawable-xhdpi/ic_search_api_mtrl_alpha.png
index 513ee8b..05cfab7 100644
--- a/core/res/res/drawable-xhdpi/ic_search_api_mtrl_alpha.png
+++ b/core/res/res/drawable-xhdpi/ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_mtrl_am_alpha.9.png b/core/res/res/drawable-xhdpi/spinner_mtrl_am_alpha.9.png
index c868ed1..d4bd169 100644
--- a/core/res/res/drawable-xhdpi/spinner_mtrl_am_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_activated_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/textfield_activated_mtrl_alpha.9.png
index 16c376a..7174b67 100644
--- a/core/res/res/drawable-xhdpi/textfield_activated_mtrl_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_default_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/textfield_default_mtrl_alpha.9.png
index e3bd904..46dad22 100644
--- a/core/res/res/drawable-xhdpi/textfield_default_mtrl_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_ab_back_mtrl_am_alpha.png b/core/res/res/drawable-xxhdpi/ic_ab_back_mtrl_am_alpha.png
index ca15853..c2d6a54 100644
--- a/core/res/res/drawable-xxhdpi/ic_ab_back_mtrl_am_alpha.png
+++ b/core/res/res/drawable-xxhdpi/ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_menu_search_mtrl_alpha.png b/core/res/res/drawable-xxhdpi/ic_menu_search_mtrl_alpha.png
index cb295a3..6f60bd3 100644
--- a/core/res/res/drawable-xxhdpi/ic_menu_search_mtrl_alpha.png
+++ b/core/res/res/drawable-xxhdpi/ic_menu_search_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_search_api_mtrl_alpha.png b/core/res/res/drawable-xxhdpi/ic_search_api_mtrl_alpha.png
index 81b13aa..6f60bd3 100644
--- a/core/res/res/drawable-xxhdpi/ic_search_api_mtrl_alpha.png
+++ b/core/res/res/drawable-xxhdpi/ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/spinner_mtrl_am_alpha.9.png b/core/res/res/drawable-xxhdpi/spinner_mtrl_am_alpha.9.png
index 7cb1486..2e7bc12 100644
--- a/core/res/res/drawable-xxhdpi/spinner_mtrl_am_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/textfield_activated_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/textfield_activated_mtrl_alpha.9.png
index a22f352..661d5f0 100644
--- a/core/res/res/drawable-xxhdpi/textfield_activated_mtrl_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/textfield_default_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/textfield_default_mtrl_alpha.9.png
index b0504e0..d7696c3 100644
--- a/core/res/res/drawable-xxhdpi/textfield_default_mtrl_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_ab_back_mtrl_am_alpha.png b/core/res/res/drawable-xxxhdpi/ic_ab_back_mtrl_am_alpha.png
new file mode 100644
index 0000000..70c2040
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_menu_search_mtrl_alpha.png b/core/res/res/drawable-xxxhdpi/ic_menu_search_mtrl_alpha.png
new file mode 100644
index 0000000..2a28f0f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_menu_search_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_search_api_mtrl_alpha.png b/core/res/res/drawable-xxxhdpi/ic_search_api_mtrl_alpha.png
new file mode 100644
index 0000000..c873e9b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/spinner_mtrl_am_alpha.9.png b/core/res/res/drawable-xxxhdpi/spinner_mtrl_am_alpha.9.png
index 2ae7234..1086e9d 100644
--- a/core/res/res/drawable-xxxhdpi/spinner_mtrl_am_alpha.9.png
+++ b/core/res/res/drawable-xxxhdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable/edit_text_material.xml b/core/res/res/drawable/edit_text_material.xml
index 86cec8f..eaf5e45 100644
--- a/core/res/res/drawable/edit_text_material.xml
+++ b/core/res/res/drawable/edit_text_material.xml
@@ -14,20 +14,25 @@
limitations under the License.
-->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?attr/colorControlActivated">
- <item>
- <selector>
- <item android:state_enabled="false">
- <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
- android:tint="?attr/colorControlNormal"
- android:alpha="?attr/disabledAlpha" />
- </item>
- <item>
- <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- </selector>
- </item>
- <item android:id="@+id/mask" android:drawable="@drawable/textfield_activated_mtrl_alpha" />
-</ripple>
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="@dimen/control_inset_material"
+ android:insetTop="@dimen/control_inset_material"
+ android:insetBottom="@dimen/control_inset_material"
+ android:insetRight="@dimen/control_inset_material">
+ <ripple android:color="?attr/colorControlActivated">
+ <item>
+ <selector>
+ <item android:state_enabled="false">
+ <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
+ android:tint="?attr/colorControlNormal"
+ android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item>
+ <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ </selector>
+ </item>
+ <item android:id="@+id/mask" android:drawable="@drawable/textfield_activated_mtrl_alpha" />
+ </ripple>
+</inset>
diff --git a/core/res/res/drawable/switch_thumb_material_anim.xml b/core/res/res/drawable/switch_thumb_material_anim.xml
index e7baa2c..67e460a 100644
--- a/core/res/res/drawable/switch_thumb_material_anim.xml
+++ b/core/res/res/drawable/switch_thumb_material_anim.xml
@@ -14,41 +14,62 @@
limitations under the License.
-->
-<animated-selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
- <item android:state_enabled="false" android:state_checked="true">
- <bitmap android:src="@drawable/btn_switch_to_on_mtrl_014" android:gravity="center" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:constantSize="true">
+ <item
+ android:state_enabled="false"
+ android:state_checked="true">
+ <bitmap
+ android:src="@drawable/btn_switch_to_on_mtrl_014"
+ android:gravity="center"
+ android:tint="?attr/colorControlActivated"
+ android:alpha="?attr/disabledAlpha" />
</item>
<item android:state_enabled="false">
- <bitmap android:src="@drawable/btn_switch_to_on_mtrl_000" android:gravity="center" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
+ <bitmap
+ android:src="@drawable/btn_switch_to_on_mtrl_000"
+ android:gravity="center"
+ android:tint="?attr/colorControlNormal"
+ android:alpha="?attr/disabledAlpha" />
</item>
- <item android:state_checked="true" android:id="@+id/on">
- <bitmap android:src="@drawable/btn_switch_to_on_mtrl_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <item
+ android:state_checked="true"
+ android:id="@+id/on">
+ <bitmap
+ android:src="@drawable/btn_switch_to_on_mtrl_014"
+ android:gravity="center"
+ android:tint="?attr/colorControlActivated" />
</item>
<item android:id="@+id/off">
- <bitmap android:src="@drawable/btn_switch_to_on_mtrl_000" android:gravity="center" android:tint="?attr/colorControlNormal" />
+ <bitmap
+ android:src="@drawable/btn_switch_to_on_mtrl_000"
+ android:gravity="center"
+ android:tint="?attr/colorControlNormal" />
</item>
- <transition android:fromId="@+id/off" android:toId="@+id/on">
+ <transition
+ android:fromId="@+id/off"
+ android:toId="@+id/on">
<animation-list>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_mtrl_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_000" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_mtrl_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_001" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_mtrl_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_002" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_mtrl_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_003" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_mtrl_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_004" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_mtrl_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_005" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_mtrl_006" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_006" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
<bitmap android:src="@drawable/btn_switch_to_on_mtrl_007" android:gravity="center" android:tint="?attr/colorControlActivated" />
@@ -100,28 +121,28 @@
<bitmap android:src="@drawable/btn_switch_to_off_mtrl_006" android:gravity="center" android:tint="?attr/colorControlActivated" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_mtrl_007" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_007" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_mtrl_008" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_008" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_mtrl_009" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_009" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_mtrl_010" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_010" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_mtrl_011" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_011" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_mtrl_012" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_012" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_mtrl_013" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_013" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
<item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_mtrl_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_014" android:gravity="center" android:tint="?attr/colorControlNormal" />
</item>
</animation-list>
</transition>
diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml
index 75b0c3d..7aa9a72 100644
--- a/core/res/res/layout/resolve_list_item.xml
+++ b/core/res/res/layout/resolve_list_item.xml
@@ -43,6 +43,7 @@
<!-- Activity name -->
<TextView android:id="@android:id/text1"
android:textAppearance="?android:attr/textAppearanceSmall"
+ android:fontFamily="sans-serif-condensed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
@@ -51,6 +52,7 @@
<!-- Extended activity info to distinguish between duplicate activity names -->
<TextView android:id="@android:id/text2"
android:textAppearance="?android:attr/textAppearanceSmall"
+ android:fontFamily="sans-serif-condensed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml
index 5acb588..b3a3478 100644
--- a/core/res/res/layout/screen_action_bar.xml
+++ b/core/res/res/layout/screen_action_bar.xml
@@ -35,6 +35,7 @@
android:layout_alignParentTop="true"
style="?attr/actionBarStyle"
android:transitionName="android:action_bar"
+ android:touchscreenBlocksFocus="true"
android:gravity="top">
<com.android.internal.widget.ActionBarView
android:id="@+id/action_bar"
@@ -53,5 +54,6 @@
android:layout_height="wrap_content"
style="?attr/actionBarSplitStyle"
android:visibility="gone"
+ android:touchscreenBlocksFocus="true"
android:gravity="center"/>
</com.android.internal.widget.ActionBarOverlayLayout>
diff --git a/core/res/res/layout/screen_toolbar.xml b/core/res/res/layout/screen_toolbar.xml
index 56815f8..039e89f 100644
--- a/core/res/res/layout/screen_toolbar.xml
+++ b/core/res/res/layout/screen_toolbar.xml
@@ -35,6 +35,7 @@
android:layout_alignParentTop="true"
style="?attr/actionBarStyle"
android:transitionName="android:action_bar"
+ android:touchscreenBlocksFocus="true"
android:gravity="top">
<Toolbar
android:id="@+id/action_bar"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index a0e4e98..0545370 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Laat die program toe om Bluetooth-MAP-boodskappe te ontvang en te verwerk. Dit beteken dat die program boodskappe wat na jou toestel gestuur word, kan monitor of uitvee sonder dat jy dit gesien het."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"haal lopende programme op"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Laat die program toe om inligting oor die huidig- en onlangslopende take op te haal. Dit kan moontlik die program toelaat om inligting oor watter programme op die toestel gebruik word, te ontdek."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interaksie tussen gebruikers"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Laat die program toe om aksies vir verskillende gebruikers op die toestel uit te voer. Kwaadwillige programme kan dit gebruik om die beskerming tussen gebruikers te skend."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"volle lisensie vir interaksie tussen gebruikers"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Laat die program toe om die blaaier se geskiedenis of boekmerke wat op jou foon gestoor is, te verander. Dit kan moontlik die program toelaat om blaaierdata uit te vee of te verander. Let wel: hierdie toestemming mag dalk nie deur derdeparty-blaaiers of ander programme met webblaaivermoëns afgedwing word nie."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"stel \'n wekker"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Laat die program toe om \'n alarm in \'n geïnstalleerde wekkerprogram te stel. Sommige wekkerprogramme werk dalk nie met hierdie funksie nie."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"skryf stemposse"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Laat die program toe om boodskappe uit jou stemposinkassie te wysig en te verwyder."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"voeg stemboodskap by"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Laat die program toe om boodskappe by te voeg by jou stempos-inkassie."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"lees stempos"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Laat die program toe om jou stemposse te lees."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"verander blaaier se geoligging-toestemmings"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Laat die program toe om die blaaier se geoligging-toestemmings te verander. Kwaadwillige programme kan dit gebruik om hulle toe te laat om ligginginligting aan enige webwerf te stuur."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verifieer pakkies"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Stelsel"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-oudio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Draadlose skerm"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Media-uitvoer"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Koppel aan toestel"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Saai skerm uit na toestel"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Soek tans vir toestelle…"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index fc9a8c8..ebbbdf0 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"መተግበሪያው የብሉቱዝ ኤምኤፒ መልእክቶችን እንዲቀበልና እንዲያካሂድ ይፈቅድለታል። ይህ ማለት መተግበሪያው ወደመሳሪያዎ የተላኩ መልእክቶችን ለእርስዎ ሳያሳይ ሊከታተል ወይም ሊሰረዝ ይችላል ማለት ነው።"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"አሂድ መተግበሪያዎችን ሰርስረው ያውጡ"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"መተግበሪያው በአሁኑ ጊዜና በቅርቡ እየተካሄዱ ስላሉ ተግባሮችን መረጃ ሰርስሮ እንዲያወጣ ይፈቅድለታል። ይህ መተግበሪያው በመሳሪያው ላይ የትኛዎቹ መተግበሪያዎች ጥቅም ላይ ስለመዋላቸው መረጃ እንዲያገኝ ሊፈቅድለት ይችላል።"</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"በተለያዩ ተጠቃሚዎች መካከል መስተጋብር መፍጠር"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"መተግበሪያው በመሣሪያው ላይ በተለያዩ ተጠቃሚዎች ላይ እርምጃዎችን እንዲፈጽም ይፈቅድለታል። ተንኮል-አዘል መተግበሪያዎች ይህንን ተጠቅመው በተጠቃሚዎች መካከል ያለውን ጥበቃ ሊጥሱ ይችላሉ።"</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"በተለያዩ ተጠቃሚዎች መካከል መስተጋብር ለመፍጠር ሙሉ ፍቃድ"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"መተግበሪያው ስልክህ ላይ የተከማቹ የአሳሹን ታሪክ ወይም ዕልባቶችን እንዲቀይር ይፈቅድለታል። ይህ መተግበሪያው የአሳሽ ውሂብ እንዲያጠፋ ወይም እንዲያስተካክል ሊፈቅድለት ይችላል። ማስታወሻ፦ ይህ ፈቃድ በሶስተኛ ወገን አሳሾች ወይም በሌላ የድር አሳሽነት አቅም ባላቸው መተግበሪያዎች ላይፈጸም ይችላል።"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"ማንቂያ አስቀምጥ"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"በተጫነው የማንቂያ ሰዓት መተግበሪያ ውስጥ ማንቅያን ለማደራጀት ለመተግበሪያው ይፈቅዳሉ፡፡አንዳንድ የማንቂያ ሰዓት መተግበሪያዎች ይሄንን ባህሪ ላይፈፅሙ ይችላሉ፡፡"</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"የድምጽ መልእክቶችን ይጻፉ"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"መተግበሪያው ከድምጽ መልእክት የገቢ መልእክት ሳጥንዎ ውስጥ መልእክቶችን እንዲያስተካክልና እንዲያስወግድ ይፈቅዳል።"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"የድምፅ መልዕክት አክል"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"ወደ ድምፅ መልዕክት የገቢ መልዕክትህ መልዕክቶች ለማከል ለመተግበሪያው ይፈቅዳሉ።"</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"የድምጽ መልእክት አንብብ"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"መተግበሪያዎ የድምጽ መልእክቶችን እንዲያነብ ይፈቅዳል።"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"የአሳሽ ገፀ ሥፍራ ፍቃዶችን ቀይር"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"የአሳሹን የጂኦ-አካባቢ ፍቃዶችን እንዲለውጥ ለመተግበሪያው ይፈቅዳል፡፡ተንኮል አዘል መተግበሪያዎች የመላኪያ አከባቢን መረጃ ወደ አጠራጣሪ የድር ጣቢያዎች ለመፍቀድ ይሄንን ሊጠቀሙበት ይችላሉ፡፡"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ፓኬጆችን አረጋግጥ"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ስርዓት"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"የብሉቱዝ ድምጽ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ገመድ አልባ ማሳያ"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"የሚዲያ ውጽዓት"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"ከመሳሪያ ጋር ያገናኙ"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"ማያ ገጽን ወደ መሣሪያ ይውሰዱ"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"መሳሪያዎችን በመፈለግ ላይ…"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 55b79e8..eb17630 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"يسمح للتطبيق بتلقي رسائل بلوتوث MAP ومعالجتها. وهذا يعني أنه سيكون بإمكان التطبيق الإشراف على أو حذف الرسائل المرسلة إليك بدون عرضها لك."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"استرداد التطبيقات التي قيد التشغيل"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"للسماح للتطبيق باسترداد معلومات حول المهام التي يجري تشغيلها حاليًا والتي تم تشغيلها مؤخرًا. وقد يسمح هذا للتطبيق باكتشاف معلومات حول التطبيقات المستخدمة على الجهاز."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"التعامل بين المستخدمين"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"للسماح للتطبيق بتنفيذ إجراءات بين مستخدمين مختلفين على الجهاز. قد تستخدم التطبيقات الضارة ذلك لانتهاك الحماية بين المستخدمين."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"الترخيص بالكامل للتعامل بين المستخدمين"</string>
@@ -1386,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"للسماح لأحد التطبيقات بالالتزام بخدمة الوكيل المعتمد."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"التفاعل مع نظام التحديث والاسترداد"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"للسماح للتطبيق بالتفاعل مع نظام الاسترداد وتحديثات النظام."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"إنشاء جلسات عرض وسائط"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"للسماح لأحد التطبيقات بإنشاء جلسات عرض وسائط. ويُمكن لهذه الجلسات تمكين التطبيقات من التقاط محتويات مصورة وأخرى صوتية. ولا حاجة إليها مع التطبيقات العادية."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"المس مرتين للتحكم في التكبير/التصغير"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"تعذرت إضافة أداة."</string>
<string name="ime_action_go" msgid="8320845651737369027">"تنفيذ"</string>
@@ -1514,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"تعديل"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"تحذير استخدام البيانات"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"المس لعرض الاستخدام والإعدادات."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2-3 غيغابايت من البيانات المعطلة"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4 غيغابايت من البيانات المعطلة"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"البيانات الخلوية معطلة"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"بيانات Wi-Fi معطلة"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"تم بلوغ الحد المعين"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"تم تجاوز حد بيانات شبكات 2G-3G"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"تم تجاوز حد بيانات 4G"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"تم تجاوز حد البيانات الخلوية"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"تم تجاوز حد بيانات شبكة Wi-Fi"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> فوق الحد المعين."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"تم تقييد بيانات الخلفية"</string>
@@ -1563,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"النظام"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"صوت بلوتوث"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"عرض شاشة لاسلكي"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"المنفذ الإعلامي"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"الاتصال بجهاز"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"بث الشاشة على الجهاز"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"جارٍ البحث عن الأجهزة…"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 437f764..8c7da0f 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Разрешава на приложението да получава и обработва съобщения чрез Bluetooth MAP. Това означава, че то може да наблюдава или изтрива изпратените до устройството ви, без да ви ги покаже."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"извличане на изпълняваните приложения"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Разрешава на приложението да извлича информация за задачите, изпълнявани понастоящем и неотдавна. Това може да му позволи да открива данни за това, кои приложения се използват на устройството."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"взаимодействие с потребителите"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Разрешава на приложението да изпълнява действия за различни потребители на устройството. Злонамерените приложения може да използват това, за да нарушат защитата между потребителите."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"пълен лиценз за взаимодействие с потребителите"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Разрешава на приложението да променя историята или отметките на браузъра, съхранени на телефона ви. Това може да му позволи да изтрива или променя данните на браузъра. Забележка: Възможно е браузъри на трети страни или други приложения с възможности за сърфиране в мрежата да не могат да наложат ограниченията на разрешението."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"навиване на будилника"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Разрешава на приложението да навие инсталирано приложение будилник. Някои будилници може да не изпълнят тази функция."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"запис на гласови съобщения"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Разрешава на приложението да променя и премахва съобщения от входящата ви гласова поща."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"добавяне на гласова поща"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Разрешава на приложението да добавя съобщения към входящата ви гласова поща."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"четене на гласовата поща"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Разрешава на приложението да чете гласовите ви съобщения."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"промяна на разрешенията за местоположение в браузъра"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Разрешава на приложението да променя разрешенията на браузъра за местоположение. Злонамерените приложения могат да използват това, за да изпращат информация за местоположението до произволни уебсайтове."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"проверка на пакетите"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Система"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Звук през Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Безжичен дисплей"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Изходяща мултимедия"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Свързване с устройство"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Екран за предаване към устройството"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Търсят се устройства…"</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 7decead..8c1318d 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Bluetooth MAP বার্তা পেতে ও প্রক্রিয়া করতে অ্যাপ্লিকেশানটিকে অনুমতি দিন। এর অর্থ হলো, অ্যাপ্লিকেশানটি আপনাকে না দেখিয়েই আপনার ডিভাইসে পাঠানো বার্তা পর্যবেক্ষণ বা মুছতে পারবে।"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"চলমান অ্যাপ্লিকেশান উদ্ধার করে"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"বর্তমানে ও সাম্প্রতিককালের সক্রিয় ক্রিয়াগুলি সম্বন্ধে তথ্য পুনরুদ্ধার করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ এছাড়া এটি ডিভাইসটিতে কোন অ্যাপ্লিকেশানগুলি ব্যবহৃত হচ্ছে তার বিষয়ে তথ্য খুঁজে বের করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করতে পারে৷"</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"সামগ্রী ব্যবহারকারীদের সাথে ইন্টারঅ্যাক্ট করুন"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"ডিভাইসটিতে থাকা বিভিন্ন ব্যবহারকারীর মধ্যে ক্রিয়াগুলির কার্য-সম্পাদনা করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ ক্ষতিকারক অ্যাপ্লিকেশানগুলি এটিকে ব্যবহারকারীদের মধ্যে সুরক্ষা লঙ্ঘন করতে ব্যবহার করতে পারে৷"</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"ব্যবহারকারীদের সাথে ইন্টারঅ্যাক্ট করার সম্পূর্ণ লাইসেন্স"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"অ্যাপ্লিকেশানটিকে আপনার ফোনে সঞ্চিত ব্রাউজারের ইতিহাস বা বুকমার্কগুলি পরিবর্তন করতে দেয়৷ এটি অ্যাপ্লিকেশানটিকে ব্রাউজার ডেটা মুছে দিতে বা পরিবর্তন করতে দেয়৷ দ্রষ্টব্য: এই অনুমতি তৃতীয় পক্ষের ব্রাউজারগুলির বা ওয়েব ব্রাউজিং ক্ষমতা সম্পন্ন অন্যান্য অ্যাপ্লিকেশানগুলি দ্বারা বলবৎ নাও হতে পারে৷"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"একটি অ্যালার্ম সেট করুন"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"অ্যাপ্লিকেশানকে একটি ইনস্টল থাকা অ্যালার্ম অ্যাপ্লিকেশানে একটি অ্যালার্ম সেট করতে দেয়৷ কিছু অ্যালার্ম ঘড়ি অ্যাপ্লিকেশানগুলিতে ভবিষ্যতে এটি লাগু নাও হতে পারে৷"</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"ভয়েসমেলগুলি লিখুন"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"অ্যাপ্লিকেশানটিকে আপনার ভয়েসমেল ইনবক্স থেকে বার্তা পরিবর্তনের ও সরানোর অনুমতি দেয়৷"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ভয়েসমেল যোগ করে"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"অ্যাপ্লিকেশানকে আপনার ভয়েসমেইল ইনবক্সে বার্তা যোগ করার অনুমতি দেয়৷"</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"ভয়েসমেল পড়ুন"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"অ্যাপ্লিকেশানটিকে আপনার ভয়েসমেলগুলি পড়ার অনুমতি দেয়।"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ব্রাউজারের ভূঅবস্থানিক অনুমতিগুলি সংশোধন করে"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"অ্যাপ্লিকেশানকে ব্রাউজারের ভূঅবস্থানিক অনুমতি সংশোধন করতে দেয়৷ ক্ষতিকারক অ্যাপ্লিকেশানগুলি নির্বিচারে ওয়েব সাইটগুলিতে অবস্থানের ডেটা পাঠানো সক্ষম করতে এটি ব্যবহার করতে পারে৷"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"প্যাকেজগুলি যাচাই করে"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"সিস্টেম"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth অডিও"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ওয়্যারলেস প্রদর্শন"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"মিডিয়া আউটপুট"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"ডিভাইসে সংযোগ করুন"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"ডিভাইসে স্ক্রীণ কাস্ট করুন"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"ডিভাইসগুলি অনুসন্ধান করা হচ্ছে…"</string>
@@ -1748,7 +1749,7 @@
<string name="lock_to_app_toast_locked" msgid="4229650395479263497">"আপনি অ্যাপ-এ-লক করুন মোডে রয়েছেন৷"</string>
<string name="lock_to_app_title" msgid="5895142291937470019">"অ্যাপ-এ-লক ব্যবহার করতে চান?"</string>
<string name="lock_to_app_description" msgid="2800403592608529611">"লক-টু-অ্যাপ কোনো একক অ্যাপ্লিকেশানে প্রদর্শন লক করে।\n \nএটি থেকে বেরিয়ে যেতে ‘সাম্প্রতিকগুলি’ বোতাম স্পর্শ করে ধরে রাখুন।"</string>
- <string name="lock_to_app_negative" msgid="2259143719362732728">"না, ধন্যবাদ"</string>
+ <string name="lock_to_app_negative" msgid="2259143719362732728">"না, থাক"</string>
<string name="lock_to_app_positive" msgid="7085139175671313864">"চালু করুন"</string>
<string name="lock_to_app_start" msgid="3074665051586318340">"অ্যাপ্লিকেশানে লক করা আছে"</string>
<string name="lock_to_app_exit" msgid="8967089657201849300">"আর অ্যাপ্লিকেশানে লক করা নেই"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index e8d3ecd..b61a863 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Permet que l\'aplicació rebi i processi missatges de Bluetooth MAP. Això vol dir que l\'aplicació pot controlar o suprimir missatges que s\'hagin enviat al teu dispositiu sense mostrar-te\'ls."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"recupera les aplicacions en execució"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permet que l\'aplicació recuperi informació sobre les tasques que s\'executen actualment i les que s\'han executat recentment. Aquesta acció pot permetre que l\'aplicació descobreixi informació sobre les aplicacions que s\'utilitzen al dispositiu."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interacciona entre usuaris"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Permet que l\'aplicació dugui a terme accions en diferents usuaris del dispositiu. Les aplicacions malicioses poden fer servir aquest permís per infringir la protecció entre usuaris."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"llicència completa per interaccionar entre usuaris"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permet que l\'aplicació modifiqui l\'historial del navegador o els marcadors del telèfon. Això pot permetre que l\'aplicació esborri o modifiqui les dades del navegador. Nota: És possible que aquest permís no s\'apliqui a navegadors de tercers o a altres aplicacions amb capacitats de navegació web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"configuració d\'una alarma"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permet que l\'aplicació defineixi una alarma en una aplicació de despertador instal·lada. És possible que algunes aplicacions de despertador no incorporin aquesta funció."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"modificar correus de veu"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Permet que l\'aplicació modifiqui i suprimeixi missatges de la safata d\'entrada de la bústia de veu."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"afegeix bústia de veu"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permet que l\'aplicació afegeixi missatges a la safata d\'entrada de la bústia de veu."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"accedir a la bústia de veu"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Permet que l\'aplicació accedeixi als correus de veu."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modifica els permisos d\'ubicació geogràfica del navegador"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permet que l\'aplicació modifiqui els permisos d\'ubicació geogràfica del navegador. Les aplicacions malicioses poden utilitzar-ho per enviar la informació d\'ubicació a llocs web arbitraris."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verifica paquets"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Àudio per Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Pantalla sense fil"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Sortida de contingut multimèdia"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Connexió al dispositiu"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Emissió de pantalla al dispositiu"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"S\'estan cercant dispositius…"</string>
@@ -1745,7 +1746,7 @@
<string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> suprimit"</string>
<string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> de la feina"</string>
<string name="lock_to_app_toast" msgid="2126866321272822564">"Estàs en mode de bloqueig d\'aplicació. Per sortir-ne, toca i mantén premut el botó Recents."</string>
- <string name="lock_to_app_toast_locked" msgid="4229650395479263497">"Estàs en el mode de bloqueig d\'aplicació."</string>
+ <string name="lock_to_app_toast_locked" msgid="4229650395479263497">"Estàs en el mode de Bloqueig d\'aplicació."</string>
<string name="lock_to_app_title" msgid="5895142291937470019">"Vols fer servir la funció Bloqueig d\'aplicació?"</string>
<string name="lock_to_app_description" msgid="2800403592608529611">"Amb Bloqueig d\'aplicació es bloqueja la pantalla en una única aplicació.\n\nPer sortir-ne, toca i mantén premut el botó Recents."</string>
<string name="lock_to_app_negative" msgid="2259143719362732728">"NO, GRÀCIES"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 3d56653..ffafe62 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Umožňuje aplikaci přijímat a zpracovat zprávy Bluetooth MAP. To znamená, že aplikace může sledovat a mazat zprávy odeslané do zařízení, aniž by vám je zobrazila."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"načtení spuštěných aplikací"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Umožňuje aplikaci získat informace o aktuálně a naposledy spuštěných úlohách. Aplikace s tímto oprávněním může odhalit informace o aplikacích, které se v zařízení používají."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interakce napříč uživateli"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Umožňuje aplikaci provádět akce napříč různými uživateli zařízení. Škodlivé aplikace toto oprávnění mohou zneužít k obejití ochrany mezi uživateli."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"úplné oprávnění k interakcím napříč uživateli"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Umožňuje aplikaci upravit historii prohlížeče nebo záložky uložené v telefonu. Aplikace s tímto oprávněním může vymazat či pozměnit data prohlížeče. Poznámka: Pro prohlížeče třetí strany a jiné aplikace umožňující procházení webu toto oprávnění platit nemusí."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"nastavení budíku"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Umožňuje aplikaci nastavit budík v nainstalované aplikaci budík. Některé aplikace budík tuto funkci nemusí obsahovat."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"zápis do hlasové schránky"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Povoluje aplikaci upravit a odebrat zprávy z hlasové schránky."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"přidat hlasovou zprávu"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Umožňuje aplikaci přidávat zprávy do hlasové schránky."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"čtení hlasových zpráv"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Povoluje aplikaci číst hlasové zprávy."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"změna oprávnění prohlížeče poskytovat informace o zeměpisné poloze"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Umožňuje aplikaci upravit oprávnění funkce geolokace v prohlížeči. Škodlivé aplikace toho mohou využít k odeslání údajů o poloze na libovolné webové stránky."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ověřit balíčky"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Umožňuje aplikaci vázat se na službu zástupce důvěryhodnosti."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interakce se systémem aktualizací a obnovení"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Umožňuje aplikaci interakci se systémem obnovení a s aktualizacemi systému."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Vytváření relací promítání médií"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Umožňuje aplikaci vytvářet relace promítání médií. Tyto relace mohou aplikacím umožnit zaznamenávat obsah obrazu a zvuku. Běžné aplikace by toto oprávnění neměly nikdy potřebovat."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Dvojitým dotykem můžete ovládat přiblížení"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget nelze přidat."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Přejít"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Upravit"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Upozornění na využití dat"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Informace o využití a nastavení"</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"Data 2G a 3G jsou vypnuta"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"Data 4G jsou vypnuta"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Mobilní data jsou vypnuta"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Data Wi-Fi jsou vypnuta"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Bylo dosaženo limitu"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Překročili jste limit dat 2G–3G"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Překročili jste limit dat 4G."</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Dosažen limit mobilních dat"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Datový limit Wi-Fi byl překročen"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> nad stanoveným limitem."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Přenos dat na pozadí je omezen"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Systém"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth Audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bezdrátový displej"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Výstup médií"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Připojení k zařízení"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Odesílání obsahu obrazovky do zařízení"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Vyhledávání zařízení…"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 299e516..5067669 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Tillader, at appen kan modtage og behandle Bluetooth MAP-beskeder. Det betyder, at appen kan overvåge eller slette de beskeder, der sendes til din enhed, uden at vise dem til dig."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"hente kørende apps"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Tillader, at appen kan hente oplysninger om nuværende og seneste opgaver. Med denne tilladelse kan appen finde oplysninger om, hvilke applikationer der bruges på enheden."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"kommunikere på tværs af brugere"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Tillader, at appen udfører handlinger på tværs af forskellige brugere på enheden. Ondsindede apps kan bruge dette til at krænke beskyttelsen mellem brugere."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"fuld licens til at kommunikere på tværs af brugere"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Tillader, at appen kan ændre browserens historik eller de bogmærker, der er gemt på din telefon. Dette kan give appen tilladelse til at slette eller ændre browserdata. Bemærk! Denne tilladelse håndhæves muligvis ikke af tredjepartsbrowsere eller andre applikationer med websøgningsfunktioner."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"indstille en alarm"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Tillader, at appen kan indstille en alarm i en installeret alarmapp. Nogle alarmapps har muligvis ikke denne funktion."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"skriv talebeskeder"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Tillader, at appen ændrer og fjerner beskeder fra indbakken for din telefonsvarer."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"tilføje telefonsvarer"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Tillader, at appen kan tilføje beskeder på din telefonsvarer."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"læs talebeskeder"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Tillader, at appen læser dine talebeskeder."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"skifte tilladelser til geografisk placering i Browser"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Tillader, at appen kan ændre browserens tilladelser angående geografisk placering. Ondsindede apps kan benytte dette til at sende oplysninger om placering til vilkårlige websites."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"bekræft pakker"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-lyd"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Trådløs skærm"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Medieudgang"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Opret forbindelse til enheden"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Send skærm til enhed"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Søger efter enheder…"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index db4ed5d..2bea178 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Ermöglicht der App, Bluetooth MAP-Mitteilungen zu empfangen und zu verarbeiten. Das bedeutet, dass die App an Ihr Gerät gesendete Nachrichten überwachen und löschen kann, ohne sie Ihnen anzuzeigen."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"Aktive Apps abrufen"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Ermöglicht der App, Informationen zu aktuellen und kürzlich ausgeführten Aufgaben abzurufen. Damit kann die App möglicherweise ermitteln, welche Apps auf Ihrem Gerät zum Einsatz kommen."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"Nutzerübergreifend interagieren"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Ermöglicht der App, auf dem Gerät nutzerübergreifend Aktionen durchzuführen. Schädliche Apps können so den zwischen den Nutzern bestehenden Schutz aufheben."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"Vollständige Lizenz zum nutzerübergreifenden Interagieren"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Ermöglicht der App, den Browserverlauf und die Lesezeichen auf Ihrem Telefon zu ändern. Damit kann die App Browserdaten löschen und ändern. Hinweis: Diese Berechtigung kann nicht von Browsern von Drittanbietern oder anderen Apps mit Internetfunktionen erzwungen werden."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"Wecker stellen"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Ermöglicht der App, einen Alarm in einer installierten Wecker-App einzurichten. Einige Wecker-Apps implementieren diese Funktion möglicherweise nicht."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"Mailboxnachrichten ändern und entfernen"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Ermöglicht dieser App, Nachrichten im Posteingang Ihrer Mailbox zu ändern und zu entfernen"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"Mailbox-Nachrichten hinzufügen"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Ermöglicht der App, Nachrichten zu Ihrem Mailbox-Posteingang hinzuzufügen"</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"Mailboxnachrichten abrufen"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Ermöglicht der App das Abrufen Ihrer Mailboxnachrichten"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Geolokalisierungsberechtigungen des Browsers ändern"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Ermöglicht der App, die Geolokalisierungsberechtigungen des Browsers zu ändern. Schädliche Apps können so Standortinformationen an beliebige Websites senden."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"Pakete überprüfen"</string>
@@ -1390,8 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Ermöglicht einer App die Anbindung an einen Trust Agent-Service"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Mit Update- und Wiederherstellungssystem interagieren"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Ermöglicht einer App die Interaktion mit dem Wiederherstellungssystem und den Systemupdates"</string>
- <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Sitzungen zum Projizieren von Medien"</string>
- <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Ermöglicht einer App, Sitzungen zum Projizieren von Medien zu erstellen. In diesen Sitzungen können Apps Bildschirm- und Audioinhalte aufnehmen. Für normale Apps sollte dies nie erforderlich sein."</string>
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Sitzungen zur Projektion von Medieninhalten erstellen"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Ermöglicht einer App, Sitzungen zur Projektion von Medieninhalten zu erstellen. In diesen Sitzungen können Apps Bildschirm- und Audioinhalte aufnehmen. Für normale Apps sollte dies nie erforderlich sein."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Für Zoomeinstellung zweimal berühren"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget konnte nicht hinzugefügt werden."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Los"</string>
@@ -1516,10 +1516,10 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Bearbeiten"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Warnung zum Datenverbrauch"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Für Verbrauch/Einstell. berühren"</string>
- <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G/3G-Daten sind deaktiviert"</string>
- <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G-Daten sind deaktiviert"</string>
- <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Mobilfunkdaten sind deaktiviert"</string>
- <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"WLAN-Daten sind deaktiviert"</string>
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G/3G-Daten deaktiviert"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G-Daten deaktiviert"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Mobilfunkdaten deaktiviert"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"WLAN-Daten deaktiviert"</string>
<string name="data_usage_limit_body" msgid="6131350187562939365">"Limit erreicht"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G-/3G-Datenlimit überschritten"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4G-Datenlimit überschritten"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-Audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Kabellose Übertragung (WiDi)"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Medienausgabe"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Mit Gerät verbinden"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Bildschirm auf Gerät übertragen"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Geräte werden gesucht…"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 5a69269..3e7c212 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Επιτρέπει στην εφαρμογή τη λήψη και την επεξεργασία μηνυμάτων MAP Bluetooth. Αυτό σημαίνει ότι η εφαρμογή θα μπορούσε να παρακολουθήσει ή να διαγράψει τα μηνύματα που αποστέλλονται στη συσκευή σας χωρίς αυτά να εμφανιστούν σε εσάς."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ανάκτηση εκτελούμενων εφαρμογών"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Επιτρέπει στην εφαρμογή την ανάκτηση πληροφοριών σχετικά με τρέχουσες και πρόσφατα εκτελούμενες εργασίες. Αυτό μπορεί να δίνει τη δυνατότητα στην εφαρμογή να ανακαλύπτει πληροφορίες σχετικά με το ποιες εφαρμογές χρησιμοποιούνται στη συσκευή."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"αλληλεπίδραση στους χρήστες"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Δίνει στην εφαρμογή τη δυνατότητα να πραγματοποιεί ενέργειες σε όλους τους διαφορετικούς χρήστες στη συσκευή. Οι κακόβουλες εφαρμογές ενδέχεται να χρησιμοποιήσουν αυτή τη δυνατότητα για να παραβιάσουν την προστασία μεταξύ των χρηστών."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"πλήρης άδεια αλληλεπίδρασης στους χρήστες"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Επιτρέπει στην εφαρμογή την τροποποίηση του ιστορικού του προγράμματος περιήγησης ή των σελιδοδεικτών που έχουν αποθηκευτεί στο τηλέφωνό σας. Αυτό μπορεί να δίνει τη δυνατότητα στην εφαρμογή να διαγράφει ή να τροποποιεί δεδομένα του προγράμματος περιήγησης. Σημείωση: αυτή η άδεια ίσως να μην μπορεί να εφαρμοστεί από τρίτα προγράμματα περιήγησης ή άλλες εφαρμογές με δυνατότητες περιήγησης ιστού."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"ρύθμιση ξυπνητηριού"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Επιτρέπει στην εφαρμογή τη ρύθμιση μιας ειδοποίησης σε μια εγκατεστημένη εφαρμογή ξυπνητηριού. Ορισμένες εφαρμογές ξυπνητηριού ενδέχεται να μην μπορούν να ενσωματώσουν αυτήν τη λειτουργία."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"εγγραφή μηνυμάτων στον τηλεφωνητή"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Επιτρέπει στην εφαρμογή να τροποποιεί και να καταργεί τα μηνύματα από τα Εισερχόμενα του τηλεφωνητή σας."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"προσθήκη τηλεφωνητή"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Επιτρέπει στην εφαρμογή να προσθέτει μηνύματα στα εισερχόμενα του αυτόματου τηλεφωνητή σας."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"ανάγνωση μηνυμάτων στον τηλεφωνητή"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Επιτρέπει στην εφαρμογή να διαβάζει τα μηνύματα στον τηλεφωνητή σας."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"τροποποίηση δικαιωμάτων γεωγραφικής θέσης του Προγράμματος περιήγησης"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Επιτρέπει στην εφαρμογή την τροποποίηση των αδειών γεωτοποθεσίας του Προγράμματος περιήγησης. Τυχόν κακόβουλες εφαρμογές ενδέχεται να το χρησιμοποιήσουν για να επιτρέψουν την αποστολή πληροφοριών τοποθεσίας σε αυθαίρετους ιστότοπους."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"επαλήθευση πακέτων"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Σύστημα"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Ήχος Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Ασύρματη οθόνη"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Έξοδος μέσων"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Σύνδεση με τη συσκευή"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Μετάδοση οθόνης σε συσκευή"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Αναζήτηση συσκευών…"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index e7c501f..376c03b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Allows the app to receive and process Bluetooth MAP messages. This means that the app could monitor or delete messages sent to your device without showing them to you."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"retrieve running apps"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Allows the app to retrieve information about currently and recently running tasks. This may allow the app to discover information about which applications are used on the device."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interact across users"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Allows the app to perform actions across different users on the device. Malicious apps may use this to violate the protection between users."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"full license to interact across users"</string>
@@ -1555,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wireless display"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Media output"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Connect to device"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Cast screen to device"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Searching for devices…"</string>
@@ -1728,7 +1733,7 @@
</plurals>
<string name="restr_pin_try_later" msgid="973144472490532377">"Try again later"</string>
<string name="immersive_mode_confirmation" msgid="7227416894979047467">"Swipe down from the top to exit full screen."</string>
- <string name="done_label" msgid="2093726099505892398">"Finished"</string>
+ <string name="done_label" msgid="2093726099505892398">"Done"</string>
<string name="hour_picker_description" msgid="6698199186859736512">"Hours circular slider"</string>
<string name="minute_picker_description" msgid="8606010966873791190">"Minutes circular slider"</string>
<string name="select_hours" msgid="6043079511766008245">"Select hours"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index e7c501f..376c03b 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Allows the app to receive and process Bluetooth MAP messages. This means that the app could monitor or delete messages sent to your device without showing them to you."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"retrieve running apps"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Allows the app to retrieve information about currently and recently running tasks. This may allow the app to discover information about which applications are used on the device."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interact across users"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Allows the app to perform actions across different users on the device. Malicious apps may use this to violate the protection between users."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"full license to interact across users"</string>
@@ -1555,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wireless display"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Media output"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Connect to device"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Cast screen to device"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Searching for devices…"</string>
@@ -1728,7 +1733,7 @@
</plurals>
<string name="restr_pin_try_later" msgid="973144472490532377">"Try again later"</string>
<string name="immersive_mode_confirmation" msgid="7227416894979047467">"Swipe down from the top to exit full screen."</string>
- <string name="done_label" msgid="2093726099505892398">"Finished"</string>
+ <string name="done_label" msgid="2093726099505892398">"Done"</string>
<string name="hour_picker_description" msgid="6698199186859736512">"Hours circular slider"</string>
<string name="minute_picker_description" msgid="8606010966873791190">"Minutes circular slider"</string>
<string name="select_hours" msgid="6043079511766008245">"Select hours"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index d5cc743..6a69085 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Permite que la aplicación reciba y procese mensajes por Bluetooth (MAP), lo que significa que podría controlar o eliminar mensajes enviados al dispositivo sin mostrártelos."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"recuperar aplicaciones en ejecución"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permite que la aplicación recupere información sobre las tareas que se estén ejecutando en ese momento o que se hayan ejecutado recientemente. La aplicación puede utilizar este permiso para descubrir cuáles son las aplicaciones que se utilizan en el dispositivo."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"Interactuar con los usuarios"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Permite que la aplicación lleve a cabo acciones entre los diferentes usuarios del dispositivo. Las aplicaciones maliciosas pueden utilizar este permiso para infringir la protección entre usuarios."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"Licencia completa para interactuar con los usuarios"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite que la aplicación modifique el historial o los marcadores del navegador almacenados en el dispositivo. La aplicación puede utilizar este permiso para borrar o modificar los datos del navegador. Nota: Este permiso no puede ser utilizado por navegadores externos ni otras aplicaciones que tengan funciones de navegación por Internet."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"programar una alarma"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite que la aplicación establezca una alarma en una aplicación de alarma instalada. Es posible que algunas aplicaciones de alarma no incluyan esta función."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"Editar los mensajes del buzón de voz"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Permite que la aplicación modifique y elimine mensajes de la bandeja de entrada del buzón de voz."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"agregar correo de voz"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite que la aplicación agregue mensajes a la bandeja de entrada de tu buzón de voz."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"Consultar los mensajes del buzón de voz"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Permite que la aplicación consulte los mensajes del buzón de voz."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modificar los permisos de ubicación geográfica del navegador"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite que la aplicación modifique los permisos de ubicación geográfica del navegador. Las aplicaciones maliciosas pueden utilizar esto para permitir el envío de información de ubicación a sitios web arbitrarios."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"Verificar paquetes"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Pantalla inalámbrica"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Salida multimedia"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Conectar al dispositivo"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Transmitir pantalla a dispositivo"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Buscando dispositivos…"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 33aaf80..bf77e1d 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Permite que la aplicación reciba y procese mensajes por Bluetooth (MAP), lo que significa que podría utilizar este permiso para controlar o eliminar mensajes enviados al dispositivo sin mostrárselos al usuario."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"recuperar aplicaciones en ejecución"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permite que aplicación recupere información sobre tareas que se están ejecutando en ese momento o que se han ejecutado recientemente. La aplicación puede utilizar este permiso para descubrir cuáles son las aplicaciones que se utilizan en el dispositivo."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interactuar con los usuarios"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Permite que la aplicación lleve a cabo acciones entre los diferentes usuarios del dispositivo. Las aplicaciones maliciosas pueden utilizar este permiso para infringir la protección entre usuarios."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"licencia completa para interactuar con los usuarios"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite que la aplicación modifique el historial o los marcadores del navegador almacenados en el teléfono. La aplicación puede utilizar este permiso para borrar o modificar datos del navegador. Nota: este permiso no pueden utilizarlo navegadores externos ni otras aplicaciones que tengan funciones de navegación por Internet."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"establecer una alarma"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite que la aplicación establezca una alarma en una aplicación de reloj instalada. Es posible que algunas aplicaciones de reloj no incluyan esta función."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"editar los mensajes de voz"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Permite que la aplicación modifique y elimine mensajes de la bandeja de entrada del buzón de voz."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"añadir buzón de voz"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite que la aplicación añada mensajes a la bandeja de entrada del buzón de voz."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"consultar los mensajes de voz"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Permite que la aplicación consulte tus mensajes de voz."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modificar los permisos de ubicación geográfica del navegador"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite que la aplicación modifique los permisos de ubicación geográfica del navegador. Las aplicaciones malintencionadas pueden usar este permiso para autorizar el envío de información sobre la ubicación a sitios web arbitrarios."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verificar paquetes"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Pantalla inalámbrica"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Salida multimedia"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Conectar a dispositivo"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Enviar pantalla a dispositivo"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Buscando dispositivos…"</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index faf51bb..4f044f3 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Võimaldab rakendusel vastu võtta ja töödelda Bluetoothi MAP-sõnumeid. See tähendab, et rakendus saab teie seadmesse saadetud sõnumeid jälgida või kustutada ilma neid teile näitamata."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"Käitatud rakenduste toomine"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Võimaldab rakendusel tuua teavet praegu ja hiljuti käitatud ülesannete kohta. See võib lubada rakendusel avastada teavet selle kohta, milliseid rakendusi seadmes kasutatakse."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"toimingud erinevatel kasutajakontodel"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Lubab rakendusel teha toiminguid seadme erinevatel kasutajakontodel. Pahatahtlikud rakendused võivad kasutada seda kasutajatevahelise kaitse rikkumiseks."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"täielik litsents teha toiminguid erinevatel kasutajakontodel"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Võimaldab rakendusel muuta telefoni salvestatud brauseri ajalugu või järjehoidjaid. See võimaldab rakendusel kustutada või muuta brauseri andmeid. Märkus: see luba ei pruugi jõustuda kolmanda osapoole brauserites või teistes veebisirvimisvõimega rakendustes."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"määrake äratus"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Võimaldab rakendusel seada installitud äratuskellarakenduses äratuse. Mõned äratuskellarakendused ei pruugi seda funktsiooni juurutada."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"kõneposti sõnumite kirjutamine"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Lubab rakendusel muuta ja kustutada kõneposti postkasti sõnumeid."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"lisa kõneposti"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Võimaldab rakendusel lisada sõnumeid teie kõneposti postkasti."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"kõneposti sõnumite lugemine"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Lubab rakendusel lugeda teie kõneposti sõnumeid."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Brauseri geolokatsiooniõiguste muutmine"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Võimaldab rakendusel muuta brauseri geolokatsiooniõigusi. Pahatahtlikud rakendused võivad seda kasutada asukohateabe saatmise lubamiseks suvalistele veebisaitidele."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"pakettide kinnitamine"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Lubab rakendusel ennast siduda usaldusväärse agendi teenusega."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Suhtlemine värskenduse ja taastesüsteemiga"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Lubab rakendusel suhelda taastesüsteemi ja süsteemivärskendustega."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Meediumi projitseerimise seansi loomine"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Lubab rakendusel luua meediumi projitsiooni seanssi. Need seansid võivad annda rakendustele võimaluse hõivata kuva- ja helisisu. Ei ole kunagi vajalik tavaliste rakenduste puhul."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Suumi juhtimiseks puudutage kaks korda"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Vidinat ei saanud lisada."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Mine"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Muuda"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Andmete kasutamise hoiatus"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Kasutuse/sätete vaat. puudutage."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G–3G andmeside on väljalülitatud"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G andmeside on väljalülitatud"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Mob. andmeside väljalülitatud"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"WiFi andmeside väljalülitatud"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Limiit on täis"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G-3G andmemahupiirang ületatud"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4G andmemahupiirang ületatud"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Mob. andmeside limiit ületatud"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"WiFi-andmete piir on ületatud"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> üle määratud piirmäära."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Taustandmed on piiratud"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Süsteem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-heli"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Juhtmeta ekraan"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Meediaväljund"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Seadmega ühendamine"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Ekraanikuva ülekandmine seadmesse"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Seadmete otsimine …"</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 67962fe..75ef543 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Bluetooth MAP mezuak jaso eta prozesatzeko baimena ematen die aplikazioei. Horrela, aplikazioek gailura bidalitako mezuak kontrola eta ezaba ditzakete, mezuak zuri erakutsi gabe."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"Eskuratu abian diren aplikazioak"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Unean edo duela gutxi exekutatutako zereginei buruzko informazioa lortzeko baimena ematen die aplikazioei. Horrela, aplikazioak gailuan erabiltzen ari diren aplikazioei buruzko informazioa ezagut dezake."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"erabiltzaileekin elkarrekintzan jardutea"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Gailuaren erabiltzaileetan ekintzak gauzatzeko baimena ematen die aplikazioei. Aplikazio gaiztoek erabil dezakete erabiltzaileen arteko babesa urratzeko."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"erabiltzaileekin elkarrekintzan jarduteko baimen osoa"</string>
@@ -443,9 +447,9 @@
<string name="permdesc_clearAppCache" product="default" msgid="2459441021956436779">"Beste aplikazioen cache-direktorioetako fitxategiak ezabatuta telefono-memorian tokia egiteko baimena ematen die aplikazioei. Hori eginez gero, beste aplikazio horiek motelago abiarazi daitezke, datuak berriro lortu beharko dituztelako."</string>
<string name="permlab_movePackage" msgid="3289890271645921411">"Aldatu tokiz aplikazioen baliabideak"</string>
<string name="permdesc_movePackage" msgid="319562217778244524">"Aplikazio-baliabideak barneko euskarritik kanpoko batera (eta alderantziz) eramatea baimentzen die aplikazioei."</string>
- <string name="permlab_readLogs" msgid="6615778543198967614">"Irakurri egunkarietako isilpeko datuak"</string>
- <string name="permdesc_readLogs" product="tablet" msgid="82061313293455151">"Sistemaren askotariko egunkari-fitxategiak irakurtzea baimentzen die aplikazioei. Horrela, tabletarekin egiten ari zarenari buruzko informazio orokorra aurki dezakete, eta isilpekoa edo pertsonala den informazioa ere barne har daiteke."</string>
- <string name="permdesc_readLogs" product="default" msgid="2063438140241560443">"Sistemaren askotariko egunkari-fitxategiak irakurtzea baimentzen die aplikazioei. Horrela, telefonoarekin egiten ari zarenari buruzko informazio orokorra aurki dezakete, eta isilpekoa edo pertsonala den informazioa ere barne har daiteke."</string>
+ <string name="permlab_readLogs" msgid="6615778543198967614">"Irakurri erregistroetako isilpeko datuak"</string>
+ <string name="permdesc_readLogs" product="tablet" msgid="82061313293455151">"Sistemaren askotariko erregistro-fitxategiak irakurtzea baimentzen die aplikazioei. Horrela, tabletarekin egiten ari zarenari buruzko informazio orokorra aurki dezakete, eta isilpekoa edo pertsonala den informazioa ere barne har daiteke."</string>
+ <string name="permdesc_readLogs" product="default" msgid="2063438140241560443">"Sistemaren askotariko erregistro-fitxategiak irakurtzea baimentzen die aplikazioei. Horrela, telefonoarekin egiten ari zarenari buruzko informazio orokorra aurki dezakete, eta isilpekoa edo pertsonala den informazioa ere barne har daiteke."</string>
<string name="permlab_anyCodecForPlayback" msgid="715805555823881818">"erreprodukziorako edozein multimedia-deskodetzaile erabiltzea"</string>
<string name="permdesc_anyCodecForPlayback" msgid="8283912488433189010">"Instalatutako edozein multimedia-deskodetzaile erabiltzeko baimena ematen die aplikazioei, gauzak erreproduzitu ahal izateko deskodetzeko."</string>
<string name="permlab_manageCaCertificates" msgid="1678391896786882014">"Kudeatu kredentzial fidagarriak"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Arakatzailearen historia edo telefonoan gordetako laster-markak aldatzeko baimena ematen die aplikazioei. Horrela, aplikazioak agian arakatzaile-datuak ezabatu edo aldatu ahal izango ditu. Oharra: baimen hori hirugarrenen arakatzaileek edo sarea arakatzeko gaitasuna eskaintzen duten bestelako aplikazioek aplika dezakete."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"alarmak ezartzea"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Instalatutako alarma batean alarmak ezartzea baimentzen die aplikazioei. Alarma-aplikazio batzuek agian ez dute eginbide hori inplementatuko."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"Idatzi ahots-mezuak"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Erantzungailuko sarrera-ontziko mezuak aldatzea eta kentzea baimentzen die aplikazioei."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"gehitu erantzungailua"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Erantzungailuko sarrera-ontzian mezuak gehitzea baimentzen die aplikazioei."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"Irakurri ahots-mezuak"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Erantzungailuko mezuak irakurtzea baimentzen die aplikazioei."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Aldatu arakatzailearen geokokapenaren baimenak"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Arakatzailearen geokokapenaren baimenak aldatzea baimentzen die aplikazioei. Aplikazio gaiztoek hori erabil dezakete kokapenari buruzko informazioa haiek hautatutako web-orrietara bidaltzeko."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"Egiaztatu paketeak"</string>
@@ -1185,11 +1185,11 @@
<string name="whichApplication" msgid="4533185947064773386">"Gauzatu ekintza hau erabilita:"</string>
<string name="whichApplicationNamed" msgid="8260158865936942783">"Osatu ekintza %1$s erabiliz"</string>
<string name="whichViewApplication" msgid="3272778576700572102">"Ireki honekin:"</string>
- <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Irekin honekin: %1$s"</string>
+ <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Irekin %1$s aplikazioarekin"</string>
<string name="whichEditApplication" msgid="144727838241402655">"Editatu honekin:"</string>
- <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Editatu honekin: %1$s"</string>
+ <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Editatu %1$s aplikazioarekin"</string>
<string name="whichSendApplication" msgid="6902512414057341668">"Partekatu honekin:"</string>
- <string name="whichSendApplicationNamed" msgid="2799370240005424391">"Partekatu honekin: %1$s"</string>
+ <string name="whichSendApplicationNamed" msgid="2799370240005424391">"Partekatu %1$s aplikazioarekin"</string>
<string name="whichHomeApplication" msgid="4616420172727326782">"Hautatu hasierako pantailako aplikazio bat"</string>
<string name="alwaysUse" msgid="4583018368000610438">"Erabili modu lehenetsian ekintza honetarako."</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"Garbitu aplikazio lehenetsia Sistemaren ezarpenak > Aplikazioak > Deskargatutakoak atalean."</string>
@@ -1390,7 +1390,7 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Konfiantza zehazteko agente baten zerbitzuari lotzea baimentzen die aplikazioei."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interaktuatu eguneratze- eta eskuratze-sistemekin"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Eskuratze-sistemarekin nahiz sistema-eguneratzeekin interaktuatzeko aukera ematen die aplikazioei."</string>
- <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Sortu multimedia-elementuak proiektatzeko saioak"</string>
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Sortu multimedia-edukia proiektatzeko saioak"</string>
<string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Multimedia-elementuak proiektatzeko saioak sortzea baimentzen die aplikazioei. Saio horiekin, aplikazioek pantailan bistaratutakoa eta audioa graba ditzakete. Aplikazio normalek ez lukete behar."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Ukitu birritan zooma kontrolatzeko"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Ezin izan da widgeta gehitu."</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetootharen audioa"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Hari gabeko pantaila"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Multimedia-irteera"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Konektatu gailura"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Igorri pantaila gailura"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Gailuak bilatzen…"</string>
@@ -1751,7 +1752,7 @@
<string name="lock_to_app_negative" msgid="2259143719362732728">"EZ, ESKERRIK ASKO"</string>
<string name="lock_to_app_positive" msgid="7085139175671313864">"HASI"</string>
<string name="lock_to_app_start" msgid="3074665051586318340">"Aplikazio batean blokeatuta"</string>
- <string name="lock_to_app_exit" msgid="8967089657201849300">"Pantaila ez dago jada aplikazio bakarrean blokeatuta"</string>
+ <string name="lock_to_app_exit" msgid="8967089657201849300">"Aplikazio bakarreko modutik irten egin da"</string>
<string name="lock_to_app_use_screen_lock" msgid="1434584309048590886">"Irten aurretik, eskatu %1$s"</string>
<string name="lock_to_app_unlock_pin" msgid="7908385370846820001">"PIN kodea"</string>
<string name="lock_to_app_unlock_pattern" msgid="7763071104790758405">"desblokeatzeko eredua"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index e38baed..468dde4 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"به برنامه اجازه میدهد پیامهای بلوتوث MAP را دریافت و پردازش کند. این یعنی برنامه میتواند پیامهای ارسالی به دستگاه شما را بدون نمایش آنها به شما حذف یا کنترل کند."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"بازیابی برنامههای در حال اجرا"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"به برنامه امکان میدهد اطلاعات مربوط به کارهای در حال اجرای اخیر و کنونی را بازیابی کند. این ممکن است به برنامه امکان دهد به اطلاعات مربوط به برنامههایی که در دستگاه استفاده میشوند دست یابد."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"ارتباط بین کاربران"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"به برنامه اجازه میدهد اقداماتی در بین کاربران مختلف در دستگاه انجام دهد. ممکن است برنامههای مخرب از این قابلیت برای نقض حفاظت موجود در بین کاربران استفاده کنند."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"مجوز کامل برای ارتباط بین کاربران"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"به برنامه اجازه میدهد سابقه مرورگر یا نشانکهای ذخیره شده در تلفن شما را اصلاح کند. این ویژگی ممکن است به برنامه اجازه دهد دادههای مرورگر را حذف یا اصلاح کند. توجه: این مجوز ممکن است توسط مرورگرهای شخص ثالث یا سایر برنامههای دارای قابلیت مرور وب قابل اجرا نباشد."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"تنظیم یک هشدار"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"به برنامه اجازه میدهد تا هشداری را در برنامه ساعت زنگدار نصب شده تنظیم کند. برخی از برنامههای ساعت زنگدار نمیتوانند این ویژگی را اعمال کنند."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"نوشتن پستهای صوتی"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"به برنامه اجازه میدهد پیامها را از صندوق ورودی پست صوتی شما تغییر داده و حذف کند."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"افزودن پست صوتی"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"به برنامه اجازه میدهد تا پیامها را به صندوق دریافت پست صوتی شما اضافه کند."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"خواندن پست صوتی"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"به برنامه اجازه میدهد پستهای صوتیتان را بخواند."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"تغییر مجوزهای مکان جغرافیایی مرورگر"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"به برنامه اجازه میدهد تا مجوزهای جغرافیایی مرورگر را تغییر دهد. برنامههای مخرب میتوانند از آن استفاده کنند تا اطلاعات موقعیت مکانی را به سایتهای وب کتابخانه بفرستند."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"تأیید بستهها"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"به یک برنامه کاربردی برای اتصال به یک سرویس trust agent اجازه میدهد."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"تعامل با سیستم بهروزرسانی و بازیابی"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"به یک برنامه کاربردی اجازه میدهد با سیستم بازیابی و بهروزرسانیهای سیستم تعامل داشته باشد."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"ایجاد جلسات فرستادن رسانه"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"به برنامه اجازه میدهد جلسههای فرستادن رسانه ایجاد کند. این جلسات میتوانند به برنامه این توانایی را بدهند که نمایشگر و محتوای صوتی را ضبط کند. هرگز نباید برای برنامههای عادی مورد نیاز باشد."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"دوبار لمس کنید تا بزرگنمایی کنترل شود"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"افزودن ابزارک انجام نشد."</string>
<string name="ime_action_go" msgid="8320845651737369027">"برو"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"ویرایش"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"هشدار میزان استفاده از داده"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"برای مشاهده کاربرد و تنظیمات لمس کنید."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"داده 2G-3G خاموش است"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"داده 4G خاموش است"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"داده شبکه سلولی خاموش است"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"داده Wi-Fi خاموش است"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"به حد مجاز رسیدهاید"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"اطلاعات 2G-3G بیش از حد مجاز است"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"بیش از حد مجاز 4G است"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"از حد مجاز شبکه سلولی فراتر رفتید"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"از محدوده مجاز دادههای Wi-Fi بیشتر شد"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> از حد تعیین شده بیشتر شد."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"داده پسزمینه محدود شد"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"سیستم"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"بلوتوثهای صوتی"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"صفحه نمایش بیسیم"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"خروجی رسانه"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"برقراری ارتباط با دستگاه"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"فرستادن صفحه نمایش به دستگاه"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"در حال جستجو برای دستگاهها..."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 4644c50..ed04c3a 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Antaa sovelluksen vastaanottaa ja käsitellä Bluetooth MAP -viestejä. Sovellus voi valvoa ja poistaa laitteeseesi lähetettyjä viestejä näyttämättä niitä sinulle."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"käynnissä olevien sovellusten noutaminen"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Antaa sovelluksen noutaa tietoja käynnissä olevista ja äskettäin suoritetuista tehtävistä. Sovellus voi saada tietoja laitteella käytetyistä sovelluksista."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"suorita käyttäjien välisiä toimintoja"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Antaa sovelluksen suorittaa käyttäjien välisiä toimintoja laitteessa. Haitalliset sovellukset voivat vahingoittaa käyttäjien välistä suojausta."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"lupa suorittaa käyttäjien välisiä toimintoja"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Antaa sovelluksen muokata selaimen historiaa ja puhelimeen tallennettuja kirjanmerkkejä. Sovellus voi poistaa tai muokata selaimen tietoja. Huomaa: kolmannen osapuolen selaimet tai muut sovellukset, jotka pystyvät selaamaan verkkoa, eivät saa käyttää tätä lupaa."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"aseta herätys"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Antaa sovelluksen asettaa hälytyksen sisäiseen herätyskellosovellukseen. Jotkin herätyskellosovellukset eivät välttämättä käytä tätä ominaisuutta."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"vastaajaviestien kirjoitus"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Antaa sovelluksen muokata ja poistaa puhelinvastaajaan saapuneita viestejä."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"lisää vastaajaviesti"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Antaa sovelluksen lisätä viestejä saapuneisiin vastaajaviesteihin."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"vastaajaviestien luku"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Antaa sovelluksen lukea vastaajaviestisi."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"selaimen maantieteellisen sijainnin lupien muokkaaminen"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Antaa sovelluksen muokata Selaimen maantieteellisen sijainnin lupia. Haitalliset sovellukset voivat sallia tällä sijaintitietojen lähettämisen mielivaltaisiin sivustoihin."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"vahvista paketteja"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Antaa sovelluksen sitoutua luotettavaan tahoon."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Vuorovaikutus päivitys- ja palautusjärjestelmän kanssa"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Sallii sovelluksen vuorovaikutuksen palautusjärjestelmän ja järjestelmäpäivitysten kanssa."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Medialähetysistuntojen luominen"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Antaa sovelluksen luoda medialähetysistuntoja. Nämä istunnot antavat sovellusten tallentaa näyttö- ja äänisisältöä. Ei tavallisten sovellusten käyttöön."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Ohjaa zoomausta napauttamalla kahdesti"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widgetin lisääminen epäonnistui."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Siirry"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Muokkaa"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Tiedonsiirtovaroitus"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Näytä käyttö ja aset. koskettam."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G–3G-tiedonsiirto ei käytössä"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G-tiedonsiirto ei käytössä"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Mobiilitiedonsiirto ei käytössä"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi-tiedonsiirto ei käytössä"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Raja saavutettu"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G-3G-tiedonsiirtoraja ylitetty"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4G-tiedonsiirtoraja ylitetty"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Mobiilitiedonsiirtoraja ylitetty"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Wi-Fi-tiedonsiirtoraja ylitetty"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> yli asetetun rajan"</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Rajoitettu taustatietojen käyttö"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Järjestelmä"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-ääni"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Langaton näyttö"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Median äänentoisto"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Yhdistä laitteeseen"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Lähetä näyttö laitteeseen"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Etsitään laitteita…"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 19db01a..39a1a9f 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Permet à l\'application de recevoir et traiter des messages par Bluetooth à l\'aide du profil MAP. Cela signifie que l\'application peut contrôler ou supprimer les messages envoyés à votre appareil sans vous les montrer."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"récupérer les données des applications en cours d\'exécution"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permet à l\'application de récupérer des données sur des tâches en cours d\'exécution et récemment exécutées. L\'application est ainsi susceptible d\'obtenir des données concernant les applications utilisées sur l\'appareil."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interagir entre les utilisateurs"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Permet à l\'application d\'effectuer des actions entre les différents utilisateurs de l\'appareil. Les applications malveillantes peuvent utiliser cette autorisation pour passer outre la protection entre les utilisateurs."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"autorisation totale d\'interagir entre les utilisateurs"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permet à l\'application de modifier l\'historique du navigateur ou les favoris enregistrés sur votre téléphone. Cette autorisation peut lui permettre d\'effacer ou de modifier les données du navigateur. Remarque : il est possible que cette autorisation ne soit pas appliquée par les navigateurs tiers ni par d\'autres applications permettant de naviguer sur le Web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"définir une alarme"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permet à l\'application de régler la sonnerie d\'une fonction de réveil installée sur votre appareil. Cette fonctionnalité n\'est pas compatible avec toutes les applications de réveils."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"modifier les messages vocaux"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Autoriser l\'application à modifier et à supprimer des messages de la boîte de réception des messages vocaux."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ajouter des messages vocaux"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permet à l\'application d\'ajouter des messages à votre messagerie vocale."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"accéder aux messages vocaux"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Autoriser l\'application à accéder à vos messages vocaux."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modifier les autorisations de géolocalisation du navigateur"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permet à l\'application de modifier les autorisations de géolocalisation du navigateur. Des applications malveillantes peuvent exploiter cette fonctionnalité pour permettre l\'envoi de données de localisation à des sites Web arbitraires."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"vérifier les paquets"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Système"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Affichage sans fil"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Sortie multimédia"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Connexion à l\'appareil"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Diffuser l\'écran sur l\'appareil"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Recherche d\'appareils en cours…"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index eea8ad9..e28d70f 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Permet à l\'application de recevoir et traiter des messages MAP Bluetooth. Cela signifie que l\'application peut contrôler ou supprimer les messages envoyés à votre appareil sans vous les montrer."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"récupérer les applications en cours d\'exécution"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permet à l\'application de récupérer des informations sur des tâches en cours d\'exécution et récemment exécutées. L\'application est ainsi susceptible d\'obtenir des informations sur les applications utilisées sur l\'appareil."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interagir entre les utilisateurs"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Permet à l\'application d\'effectuer des actions entre les différents utilisateurs de l\'appareil. Les applications malveillantes peuvent utiliser cette autorisation pour passer outre la protection entre les utilisateurs."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"autorisation totale d\'interagir entre les utilisateurs"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permet à l\'application de modifier l\'historique du navigateur ou les favoris enregistrés sur votre téléphone. Cette autorisation peut lui permettre d\'effacer ou de modifier les données du navigateur. Remarque : il est possible que cette autorisation ne soit pas appliquée par les navigateurs tiers ni par d\'autres applications permettant de naviguer sur le Web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"définir une alarme"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permet à l\'application de régler la sonnerie d\'un réveil installé. Cette fonctionnalité n\'est pas disponible sur tous les réveils."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"modifier les messages vocaux"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Autoriser l\'application à modifier et à supprimer des messages de la boîte de réception des messages vocaux"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ajouter un message vocal"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permet à l\'application d\'ajouter des messages à votre messagerie vocale."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"accéder à la messagerie vocale"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Autoriser l\'application à accéder à votre messagerie vocale"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modifier les autorisations de géolocalisation du navigateur"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permet à l\'application de modifier les autorisations de géolocalisation du navigateur. Des applications malveillantes peuvent exploiter cette fonctionnalité pour permettre l\'envoi de données de localisation à des sites Web arbitraires."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"vérifier les packages"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Système"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Affichage sans fil"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Sortie multimédia"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Connexion à l\'appareil"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Caster l\'écran sur l\'appareil"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Recherche d\'appareils en cours…"</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 3896c16..6f9e9e7 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Permite que a aplicación reciba e procese as mensaxes de MAP por Bluetooth. Isto quere dicir que a aplicación podería controlar ou eliminar as mensaxes enviadas ao teu dispositivo sen mostrarchas."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"recuperar aplicacións en execución"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permite á aplicación recuperar información acerca de tarefas que se están executando actualmente ou que se executaron recentemente. É posible que esta acción permita á aplicación descubrir información acerca de que aplicacións se utilizan no dispositivo."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interactuar entre os usuarios"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Permite á aplicación levar a cabo accións nos diferentes usuarios do dispositivo. É posible que aplicacións maliciosas utilicen isto para vulnerar a protección entre os usuarios."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"licenza completa para interactuar entre os usuarios"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite á aplicación modificar o historial ou os favoritos do navegador que están almacenados no teu teléfono. É posible que esta acción permita á aplicación borrar ou modificar os datos do navegador. Nota: É posible que este permiso non sexa executado por navegadores de terceiros ou outras aplicacións con funcionalidades de navegación web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"definir unha alarma"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite á aplicación definir unha alarma nunha aplicación de espertador instalada. É posible que algunhas aplicacións de espertador non implementen esta función."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"escribir correos de voz"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Permite á aplicación modificar e eliminar mensaxes da caixa de entrada do teu correo de voz."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"engadir correo de voz"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite á aplicación engadir mensaxes á caixa de entrada do teu correo de voz."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"ler correo de voz"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Permite á aplicación ler os teus correos de voz."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modificar os permisos de xeolocalización do navegador"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite á aplicación modificar os permisos de xeolocalización do navegador. É posible que as aplicacións maliciosas utilicen esta opción para permitir o envío de información de localización a sitios web arbitrarios."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verificar paquetes"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio por Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Visualización sen fíos"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Saída multimedia"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Conectar co dispositivo"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Emisión de pantalla no dispositivo"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Buscando dispositivos…"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index a82e542..6bf9c70 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"ऐप्स को Bluetooth MAP संदेशों को प्राप्त करने और भेजने देती है. इसका अर्थ है कि ऐप्स आपके उपकरण पर भेजे गए संदेशों को आपको दिखाए बिना ही मॉनीटर कर सकता है या उन्हें हटा सकता है."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"चल रहे ऐप्स पुनर्प्राप्त करें"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"ऐप्स को वर्तमान में और हाल ही में चल रहे कार्यों के बारे में जानकारी को पुन: प्राप्त करने देता है. इससे ऐप्स उपकरण पर उपयोग किए गए ऐप्स के बारे में जानकारी खोज सकता है."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"उपयोगकर्ताओं के बीच सहभागिता करें"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"ऐप्स को उपकरण पर भिन्न उपयोगकर्ताओं के बीच कार्य निष्पादित करने देता है. दुर्भावनापूर्ण ऐप्स उपयोगकर्ताओं के बीच सुरक्षा का उल्लंघन करने के लिए इसका उपयोग कर सकते हैं."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"उपयोगकर्ताओं के बीच सहभागिता करने के लिए पूर्ण लाइसेंस"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"ऐप्स को आपके फ़ोन में संग्रहीत ब्राउज़र के इतिहास या बुकमार्क को संशोधित करने देता है. इससे ऐप्स ब्राउज़र डेटा को मिटा सकता है या संशोधित कर सकता है. ध्यान दें: यह अनुमति तृतीय-पक्ष ब्राउज़र या वेब ब्राउज़िंग क्षमताओं वाले अन्य ऐप्स द्वारा लागू नहीं की जा सकती."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"अलार्म सेट करें"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"ऐप्स को इंस्टॉल किए गए अलार्म घड़ी ऐप्स में अलार्म सेट करने देता है. हो सकता है कुछ अलार्म घड़ी ऐप्स में यह सुविधा न हो."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"ध्वनिमेल लिखें"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"ऐप्स को आपके ध्वनिमेल इनबॉक्स के संदेशों को बदलने और निकालने देती है."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ध्वनिमेल जोड़ें"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"ऐप्स को आपके ध्वनिमेल इनबॉक्स में संदेश जोड़ने देता है."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"ध्वनिमेल पढ़ें"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"ऐप्स को आपका ध्वनिमेल पढ़ने देती है."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ब्राउज़र भौगोलिक-स्थान अनुमतियों को बदलें"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ऐप्स को ब्राउज़र के भौगोलिक-स्थान की अनुमतियां संशोधित करने देता है. दुर्भावनापूर्ण ऐप्स इसका उपयोग एकपक्षीय वेबसाइट को स्थान जानकारी भेजने में कर सकते हैं."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"पैकेज सत्यापित करें"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"सिस्टम"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth ऑडियो"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"वायरलेस प्रदर्शन"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"मीडिया आउटपुट"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"उपकरण से कनेक्ट करें"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"स्क्रीन को उपकरण में कास्ट करें"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"उपकरण खोजे जा रहे हैं…"</string>
@@ -1752,7 +1753,7 @@
<string name="lock_to_app_positive" msgid="7085139175671313864">"प्रारंभ करें"</string>
<string name="lock_to_app_start" msgid="3074665051586318340">"ऐप्स-पर-लॉक किया गया"</string>
<string name="lock_to_app_exit" msgid="8967089657201849300">"अब ऐप्स-पर-लॉक नहीं है"</string>
- <string name="lock_to_app_use_screen_lock" msgid="1434584309048590886">"बाहर निकलने से पहले %1$s से पूछें"</string>
+ <string name="lock_to_app_use_screen_lock" msgid="1434584309048590886">"बाहर निकलने से पहले %1$s के लिए पूछें"</string>
<string name="lock_to_app_unlock_pin" msgid="7908385370846820001">"पिन"</string>
<string name="lock_to_app_unlock_pattern" msgid="7763071104790758405">"पैटर्न अनलॉक करें"</string>
<string name="lock_to_app_unlock_password" msgid="795224196583495868">"पासवर्ड"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 3d11cd3..a01d72d 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Aplikaciji omogućuje primanje i obradu Bluetooth MAP poruka. To znači da aplikacija može nadzirati ili brisati poruke poslane na vaš uređaj, a da vam ih ne prikaže."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"dohvaćanje pokrenutih aplikacija"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Aplikaciji omogućuje dohvaćanje informacija o trenutačnim i nedavnim tekućim zadacima. To aplikaciji može omogućiti otkrivanje informacija o tome koje se aplikacije upotrebljavaju na uređaju."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interakcija među korisnicima"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Omogućuje aplikaciji izvršavanje radnji među korisnicima na uređaju. Zlonamjerne aplikacije mogu to iskoristiti za narušavanje zaštite među korisnicima."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"dozvola za potpunu interakciju među korisnicima"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Aplikaciji omogućuje izmjenu povijesti i oznaka Preglednika pohranjenih na telefonu. To aplikaciji može omogućiti brisanje ili izmjenu podataka Preglednika. Napomena: tu dozvolu ne mogu primijeniti preglednici treće strane ili druge aplikacije s mogućnostima pregledavanja weba."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"postavljanje alarma"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Omogućuje aplikaciji postavljanje alarma na instaliranoj aplikaciji budilici. Neke aplikacije budilice možda neće primijeniti tu značajku."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"pisanje govorne pošte"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Omogućuje aplikaciji izmjenjivanje i uklanjanje poruka iz dolazne govorne pošte."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"dodaj govornu poštu"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Omogućuje aplikaciji da doda poruke u vašu govornu poštu."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"čitanje govorne pošte"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Omogućuje aplikaciji čitanje vaše govorne pošte."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"izmjena dozvola za geolociranje u pregledniku"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Omogućuje aplikaciji promjenu geolokacijskih dozvola preglednika. Zlonamjerne aplikacije mogu to upotrijebiti da bi dopustile slanje podataka o lokaciji nasumičnim web-lokacijama."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"provjeri pakete"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Omogućuje aplikaciji povezivanje s uslugom pouzdanog predstavnika."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interakcija s ažuriranjem i sustavom za oporavak"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Omogućuje aplikaciji interakciju sa sustavom za oporavak i ažuriranjima sustava."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Stvaranje sesija za projiciranje medija"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Omogućuje aplikaciji stvaranje sesija za projiciranje medija. Te sesije mogu omogućiti aplikaciji snimanje zaslona i audiosadržaja. Ne bi trebalo biti potrebno za uobičajene aplikacije."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Dodirnite dvaput za upravljanje zumiranjem"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget nije moguće dodati."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Idi"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Uredi"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Upozorenje o upotrebi podataka"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Dod. za prikaz upotrebe i post."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G-3G podaci isključeni"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G podaci isključeni"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Mobilni podaci isključeni"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi podaci isključeni"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Ograničenje dosegnuto"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Prekoračeno ograničenje 2G-3G"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Prekoračeno je ograničenje 4G podataka"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Limit mobilnih podataka premašen"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Prekorač. Wi-Fi ogranič. pod."</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"Veličina <xliff:g id="SIZE">%s</xliff:g> prelazi navedeno ograničenje."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Pozadinski podaci ograničeni"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sustav"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth zvuk"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bežični prikaz"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Medijski izlaz"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Povezivanje s uređajem"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Emitiranje zaslona na uređaj"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Traženje uređaja…"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index c9570d2..68a5dff 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Lehetővé teszi az alkalmazás számára, hogy Bluetooth MAP üzeneteket fogadjon és dolgozzon fel. Ez azt jelenti, hogy az alkalmazás anélkül figyelheti meg vagy törölheti a beérkező üzeneteket, hogy megjelenítené azokat Önnek."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"futó alkalmazások lekérése"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Lehetővé teszi az alkalmazás számára a jelenleg futó és nemrég befejezett feladatokkal kapcsolatos információk lekérését. Ezáltal az alkalmazás engedélyt kap az eszközön használt alkalmazásokkal kapcsolatos információk felderítésére."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"felhasználók közötti interakció"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Lehetővé teszi az alkalmazás számára, hogy több felhasználó között végezzen különféle műveleteket az eszközön. A rosszindulatú alkalmazások arra használhatják ezt, hogy megsértsék a felhasználók biztonságát."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"teljes licenc a felhasználók közötti interakcióhoz"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Lehetővé teszi az alkalmazás számára, hogy módosítsa a böngésző előzményeit vagy a telefonon tárolt könyvjelzőket. Az engedéllyel rendelkező alkalmazás törölheti vagy módosíthatja a böngésző adatait. Megjegyzés: előfordulhat, hogy ezt az engedélyt harmadik felek által üzemeltetett böngészők vagy egyéb böngészésre képes alkalmazások nem léptetik életbe."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"ébresztés beállítása"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Lehetővé teszi az alkalmazás számára, hogy ébresztőt állítson be egy telepített ébresztőóra alkalmazásban. Egyes ilyen alkalmazásokban lehet, hogy nem működik ez a funkció."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"a hangüzenetek módosítása"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Lehetővé teszi az alkalmazás számára, hogy üzeneteket módosítson vagy távolítson el a bejövő hangpostafiókból."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"hangposta hozzáadása"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Lehetővé teszi az alkalmazás számára, hogy üzeneteket adjon hozzá bejövő hangpostájához."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"a hangüzenetek olvasása"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Engedélyezi az alkalmazásnak a hangüzenetek olvasását."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"a böngésző helymeghatározási engedélyeinek módosítása"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Lehetővé teszi az alkalmazás számára, hogy módosítsa a böngésző helymeghatározási engedélyeit. Rosszindulatú alkalmazások ezt arra használhatják, hogy a helyére vonatkozó információkat küldjenek tetszőleges webhelyeknek."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"csomagok ellenőrzése"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Rendszer"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth hang"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Vezeték nélküli kijelző"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Médiakimenet"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Csatlakozás adott eszközhöz"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Képernyő átküldése az eszközre"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Eszközkeresés…"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 942a279..7bf6207 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Թույլ է տալիս հավելվածին ստանալ և մշակել Bluetooth MAP հաղորդագրությունները: Սա նշանակում է, որ հավելվածը կարող է ստուգել կամ ջնջել ձեր սարքին ուղարկված հաղորդագրությունները` առանց դրանք ձեզ ցուցադրելու:"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"առբերել աշխատող հավելվածները"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Թույլ է տալիս հավելվածին առբերել մանրամասն տեղեկություններ առկա և վերջերս աշխատող առաջադրանքների մասին: Սա կարող է թույլ տալ հավելվածին հայտնաբերել անձնական տեղեկություններ այլ հավելվածների վերաբերյալ:"</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"հաղորդակցվել օգտվողների միջև"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Թույլ է տալիս հավելվածին իրականացնել գործողություններ սարքի տարբեր օգտվողների միջոցով: Վնասարար հավելվածները կարող են օգտագործել սա` խախտելու օգտվողների միջև պաշտպանությունը:"</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"ամբողջական հաղորդակցվելու արտոնություն օգտվողների միջև"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Թույլ է տալիս հավելվածին փոփոխել դիտարկչի պատմությունը կամ ձեր հեռախոսում պահված էջանիշերը: Այն կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկչի տվյալները: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"դնել ազդանշան"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Թույլ է տալիս հավելվածին սահմանել զարթուցիչի ծրագրում տեղադրված ազդանշանը: Զարթուցիչի որոշ հավելվածներ չեն կարող կիրառել այս հատկությունը:"</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"գրել ձայնային փոստ"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Ծրագրին թույլ է տալիս մուտքի արկղից փոփոխել և հեռացնել ձեր ձայնային փոստը:"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ավելացնել ձայնային փոստ"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Թույլ է տալիս հավելվածին ավելացնել հաղորդագրություններ ձեր ձայնային փոստի արկղում:"</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"կարդալ ձայնային փոստը"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Ծրագրին թույլ է տալիս կարդալ ձեր ձայնային փոստը"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"փոփոխել դիտարկչի աշխարհագրական տեղանքի թույլտվությունները"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Թույլ է տալիս հավելվածին փոփոխել զննարկչի աշխարհագրական դիրքի թույլտվությունները: Վնասարար հավելվածները կարող են օգտագործել սա` թույլատրելու ուղարկել տեղադրության վերաբերյալ տեղեկությունները կամայական վեբ կայքերին:"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"հաստատել փաթեթները"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Ծրագրին թույլ է տալիս կապվել վստահելի գործակալի ծառայությանը:"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Փոխազդել թարմացման և վերականգնման համակարգի հետ"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Թույլ է տալիս ծրագրին փոխազդել վերականգնման համակարգի և համակարգի թարմացումների հետ:"</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Ստեղծել մեդիայի տեսարձակման աշխատաշրջաններ"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Ծրագրին թույլ է տալիս ստեղծել մեդիայի տեսարձակման աշխատաշրջաններ: Այդ աշխատաշրջանները կարող են ծրագրերին թույլ տալ հավաքագրել էկրանի և աուդիոյի բովանդակությունը: Սովորական հավելվածների համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Հպեք երկու անգամ` դիտափոխման կարգավորման համար"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Չհաջողվեց վիջեթ ավելացնել:"</string>
<string name="ime_action_go" msgid="8320845651737369027">"Առաջ"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Խմբագրել"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Տվյալների օգտագործման նախազգուշացում"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Հպեք` օգտագործումը և կարգավորումները տեսնելու համար:"</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G-3G տվյալների կապն անջատված է"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G տվյալների կապն անջատված է"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Բջջային տվյալներն անջատված են"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi տվյալներն անջատված են"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Սահմանաչափը սպառվեց"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G-3G տվյալների սահմանը գերազանցված է"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4G տվյալների սահմանը գերազանցվել է"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Բջջային տվյալների չափը սպառվեց"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Wi-Fi տվյալների սահմանը գերազանցվել է"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g>-ը գերազանցում է նշված սահմանաչափը:"</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Հետնաշերտային տվյալները սահմանափակ են"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Համակարգ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-ի ձայնանյութ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Անլար էկրան"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Մեդիա արտածում"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Միանալ սարքին"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Հեռարձակել էկրանը սարքի վրա"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Որոնվում են սարքեր..."</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index cf020122..a730c8d 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Mengizinkan aplikasi menerima dan memproses pesan MAP Bluetooth. Artinya, aplikasi dapat memantau atau menghapus pesan yang dikirim ke perangkat Anda tanpa menunjukkannya kepada Anda."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"mengambil apl yang berjalan"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Memungkinkan aplikasi mengambil informasi tentang tugas yang dijalankan saat ini dan baru-baru ini. Izin ini memungkinkan aplikasi menemukan informasi tentang aplikasi mana yang digunakan pada perangkat."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"berinteraksi antar-pengguna"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Mengizinkan aplikasi melakukan tindakan antar-pengguna yang berbeda pada perangkat. Aplikasi berbahaya dapat menggunakan ini untuk mengganggu perlindungan antar-pengguna."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"lisensi penuh untuk berinteraksi antar-pengguna"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Memungkinkan aplikasi mengubah riwayat atau bookmark Browser yang tersimpan dalam ponsel Anda. Izin ini memungkinkan aplikasi menghapus atau mengubah data Browser. Catatan: izin ini tidak dapat diberlakukan oleh browser pihak ketiga atau aplikasi lain dengan kemampuan menjelajahi web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"setel alarm"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Mengizinkan apl menyetel alarm di apl jam alarm yang terpasang. Beberapa apl jam alarm mungkin tidak menerapkan fitur ini."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"tulis kotak pesan"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Memungkinkan aplikasi memodifikasi dan membuang pesan dari kotak masuk pesan suara Anda."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"tambahkan kotak pesan"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Mengizinkan apl menambahkan pesan ke kotak masuk untuk pesan suara Anda."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"baca kotak pesan"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Memungkinkan aplikasi membaca kotak pesan Anda."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"memodifikasi izin geolokasi Browser"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Mengizinkan apl memodifikasi izin geolokasi Browser. Apl berbahaya dapat menggunakan izin ini untuk memungkinkan pengiriman informasi lokasi ke sembarang situs web."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verifikasi paket"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Mengizinkan aplikasi mengikat ke layanan agen kepercayaan."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Berinteraksi dengan sistem pemulihan dan pembaruan"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Mengizinkan aplikasi berinteraksi dengan sistem pemulihan dan pembaruan sistem."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Membuat sesi proyeksi media"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Memungkinkan aplikasi membuat sesi proyeksi media. Sesi ini dapat memberikan kemampuan pada aplikasi untuk menangkap konten audio dan layar. Tidak diperlukan oleh aplikasi normal."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Sentuh dua kali untuk mengontrol perbesar/perkecil"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Tidak dapat menambahkan widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Buka"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Edit"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Peringatan penggunaan data"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Sentuh utk mlht pnggnaan & stln."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"Data 2G-3G nonaktif"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"Data 4G nonaktif"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Data seluler nonaktif"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Data Wi-Fi nonaktif"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Batas tercapai"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Batas data 2G-3G terlampaui"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Batas data 4G terlampaui"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Batas data seluler terlampaui"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Batas data Wi-Fi terlampaui"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> melebihi batas yang ditentukan."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Data latar belakang dibatasi"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Layar nirkabel"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Keluaran media"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Sambungkan ke perangkat"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Transmisi layar ke perangkat"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Menelusuri perangkat…"</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 270fb8c..564b4e8 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Leyfir forritinu að taka á móti og vinna úr Bluetooth MAP-skilaboðum. Þetta þýðir að forritið getur fylgst með eða eytt skilaboðum sem send eru í tækið án þess að sýna þér þau."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"sækja forrit í gangi"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Leyfir forriti að sækja upplýsingar um opin forrit og forrit sem nýlega hafa verið opin. Þetta getur gert forritinu kleift að nálgast upplýsingar um forritin sem notuð eru í tækinu."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"samskipti á milli notenda"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Leyfir forriti að framkvæma aðgerðir á milli notenda tækisins. Spilliforrit geta notað þetta til að brjóta á bak aftur vörn á milli notenda."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"fullt leyfi til að eiga í samskiptum notenda á milli"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Leyfir forriti að breyta vafraferlinum eða bókamerkjum sem vistuð eru í símanum. Þetta getur gert forritinu kleift að eyða eða breyta vafragögnum. Athugaðu: Ekki er víst að þessi heimild sé virt í vöfrum frá þriðja aðila eða öðrum forritum með vafraeiginleika."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"stilla vekjara"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Leyfir forriti að stilla vekjara á uppsettri vekjaraklukku. Verið getur að sum vekjaraklukkuforrit séu ekki búin þessum eiginleika."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"skrifa talhólfsskilaboð"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Leyfir forritinu að breyta og fjarlægja skilaboð úr talhólfinu."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"bæta við talhólfi"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Leyfir forriti að bæta skilaboðum við talhólfið þitt."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"lesa talhólfsskilaboð"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Leyfir forritinu að lesa talhólfsskilaboðin þín."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"breyta staðsetningarheimildum vafrans"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Leyfir forriti að breyta heimildum vafrans fyrir landfræðilega staðsetningu. Spilliforrit geta notað þetta til að leyfa sendingu staðsetningarupplýsinga á vafasöm vefsvæði."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"staðfesta pakka"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Kerfi"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-hljóð"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Þráðlaus skjábirting"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Margmiðlunarúttak"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Tengjast tæki"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Senda skjá út í tæki"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Leitar að tækjum…"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 1241118..8d7a210 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Consente all\'app di ricevere ed elaborare i messaggi Bluetooth MAP. Questo significa che l\'app può monitorare o eliminare i messaggi inviati al tuo dispositivo senza mostrarteli."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"recupero applicazioni in esecuzione"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Consente all\'applicazione di recuperare informazioni sulle attività attualmente e recentemente in esecuzione. Ciò potrebbe consentire all\'applicazione di scoprire informazioni sulle applicazioni in uso sul dispositivo."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interazione tra gli utenti"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Consente all\'applicazione di compiere azioni per diversi utenti sul dispositivo. Le applicazioni dannose potrebbero farne uso per violare la protezione tra utenti."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"licenza completa per l\'interazione tra utenti"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Consente all\'applicazione di modificare la cronologia o i segnalibri del Browser memorizzati sul telefono. Ciò potrebbe consentire all\'applicazione di cancellare o modificare i dati del Browser. Nota. È possibile che questa autorizzazione non sia applicabile da browser di terze parti o altre applicazioni con funzionalità di navigazione web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"impostazione sveglia"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Consente all\'applicazione di impostare una sveglia in un\'applicazione sveglia installata. È possibile che alcune applicazioni sveglia non possano implementare questa funzione."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"scrivi messaggi vocali"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Consente all\'app di modificare e rimuovere messaggi dalla segreteria."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"aggiunta di un messaggio vocale"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Consente all\'applicazione di aggiungere messaggi alla casella della segreteria."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"leggi messaggi vocali"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Consente all\'app di leggere i messaggi vocali."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modifica delle autorizzazioni di localizzazione geografica del browser"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Consente all\'applicazione di modificare le autorizzazioni di geolocalizzazione del Browser. Le applicazioni dannose potrebbero farne uso per consentire l\'invio di informazioni sulla posizione a siti web arbitrari."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verifica dei pacchetti"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Visualizzazione wireless"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Uscita media"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Connetti al dispositivo"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Trasmetti schermo al dispositivo"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Ricerca di dispositivi in corso…"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index cba8770..82d9242 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"הרשאה זו מאפשרת לאפליקציה לקבל ולעבד הודעות MAP של Bluetooth. משמעות הדבר היא שהאפליקציה יכולה לעקוב אחר הודעות הנשלחות למכשיר שלך או למחוק אותן מבלי להראות לך אותן."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"אחזור אפליקציות פעילות"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"מאפשר לאפליקציה לאחזר מידע לגבי משימות הפועלות כרגע ושפעלו לאחרונה. ייתכן שהדבר יתיר לאפליקציה לגלות מידע לגבי האפליקציות שבהן נעשה שימוש במכשיר."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"אינטראקציה בין משתמשים"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"מאפשר לאפליקציה לבצע פעולות בין משתמשים שונים במכשיר. אפליקציות זדוניות עשויות להשתמש ביכולת זו כדי לפרוץ את ההגנה בין משתמשים."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"רישיון מלא לבצע אינטראקציה בין משתמשים"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"מאפשר לאפליקציה לשנות את ההיסטוריה או ה-Bookmarks של הדפדפן המאוחסנים בטלפון. הדבר עשוי לאפשר לאפליקציה למחוק או לשנות נתוני דפדפן. שים לב: אישור זה אינו ניתן לאכיפה על ידי דפדפני צד שלישי או אפליקציות אחרות בעלות יכולות גלישה באינטרנט."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"הגדרת התראה"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"מאפשר לאפליקציה להגדיר התראה באפליקציה מותקנת של שעון מעורר. אפליקציות מסוימות של שעון מעורר אינן מיישמות תכונה זו."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"כתיבת הודעות דואר קולי"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"מאפשרת לאפליקציה לשנות ולהסיר הודעות מתיבת הדואר הנכנס של דואר קולי."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"הוסף דואר קולי"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"מאפשר לאפליקציה להוסיף הודעות לתיבת הדואר הקולי."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"קריאת דואר קולי"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"מאפשרת לאפליקציה לקרוא את הודעות הדואר הקולי שלך."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"שינוי הרשאות המיקום הגיאוגרפי של הדפדפן"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"מאפשר לאפליקציה לשנות את הרשאות המיקום הגיאוגרפי של הדפדפן. אפליקציות זדוניות עלולות להשתמש בכך כדי לאפשר משלוח של פרטי מיקום לאתרים זדוניים אחרים."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"אימות חבילות"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"מערכת"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"אודיו Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"צג אלחוטי"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"פלט מדיה"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"התחברות למכשיר"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"העברת מסך אל מכשיר"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"מחפש מכשירים…"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 73b9f94..aa7774d 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Bluetooth MAPメッセージの受信と処理をアプリに許可します。これにより、端末に届いたメッセージをアプリが表示することなく監視または削除するおそれがあります。"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"実行中のアプリの取得"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"現在実行中または最近実行したタスクに関する情報の取得をアプリに許可します。これにより、その端末でどのアプリを使用しているかをアプリから識別できるようになる可能性があります。"</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"ユーザー間の交流"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"端末上の各ユーザーに対して操作を実行することをアプリに許可します。この許可を悪意のあるアプリに利用されると、ユーザー間の保護が侵害される恐れがあります。"</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"ユーザー間で交流するための完全ライセンス"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"携帯端末に保存されているブラウザの履歴やブックマークの変更をアプリに許可します。これにより、アプリがブラウザデータを消去または変更できるようになる可能性があります。注: この許可は、サードパーティブラウザまたはウェブブラウジング機能を備えたその他のアプリでは適用されない場合があります。"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"アラームの設定"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"インストール済みアラームアプリのアラームを設定することをアプリに許可します。この機能が実装されていないアラームアプリもあります。"</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"ボイスメールの書き込み"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"ボイスメール受信トレイからメッセージを修正、削除することをアプリに許可します。"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ボイスメールの追加"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"ボイスメール受信トレイにメッセージを追加することをアプリに許可します。"</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"ボイスメールの読み取り"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"ボイスメールの読み取りをアプリに許可します。"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ブラウザの現在地情報に対する権限の変更"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ブラウザの現在地情報に対する権限の変更をアプリに許可します。この許可を悪意のあるアプリに利用されると、任意のウェブサイトに現在地情報が送信される恐れがあります。"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"パッケージのベリファイ"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"信頼できるエージェントサービスにバインドすることをアプリに許可します。"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"アップデートと回復システムへのアクセス"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"回復システムとシステムアップデートへのアクセスをアプリに許可します。"</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"メディアプロジェクションセッションの作成"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"メディアプロジェクションセッションの作成をアプリに許可します。これらのセッションでは、アプリがディスプレイ/オーディオコンテンツをキャプチャできます。通常のアプリでは不要です。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ダブルタップでズームコントロール"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ウィジェットを追加できませんでした。"</string>
<string name="ime_action_go" msgid="8320845651737369027">"移動"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"編集"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"データ使用の警告"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"タップして使用状況と設定を表示します。"</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G~3Gデータが無効になりました"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4Gデータが無効になりました"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"モバイルデータが無効になりました"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fiデータが無効になりました"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"上限に達しました"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G~3Gデータの上限を超えました"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4Gデータの上限を超えました"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"モバイルデータの上限を超えました"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Wi-Fiデータの上限を超えました"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"指定した上限を<xliff:g id="SIZE">%s</xliff:g>超えました。"</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"バックグラウンドデータに上限あり"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"システム"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth音声"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ワイヤレスディスプレイ"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"メディア出力"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"端末に接続"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"端末への画面のキャスト"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"端末を検索しています…"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 342887e..cb3f60f 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"აპს შეეძლება Bluetooth MAP შეტყობინებების მიღება და დამუშავება. ეს ნიშნავს, რომ აპს შეეძლება შეტყობინებების მონიტორინგი და მათი წაშლა თქვენთვის ჩვენების გარეშე."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"მოქმედი აპების მოძიება"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"აპს შეეძლება მოიძიოს ინფორმაცია ამჟამად და უახლოეს წარსულში მიმდინარე ამოცანების შესახებ. ამგვარად, აპს აქვს შესაძლებლობა აღმოაჩინოს ინფორმაცია იმის შესახებ, თუ რომელი აპლიკაციებია გამოყენებული მოწყობილობაზე."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"მომხმარებლებს შორის ინტერაქცია"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"აპს შეეძლება, სხვადასხვა მომხმარებლის მოქმედებები შეასრულოს მოწყობილობაზე. მავნე აპებმა შეიძლება მომხმარებლებს შორის დაცვის დასარღვევად გამოიყენონ."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"მომხმარებლებთან ინტერაქციის სრული ლიცენზია"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"აპს შეეძლება, შეცვალოს ბრაუზერის ისტორია და თქვენ ტელეფონში შენახული სანიშნეები. ამან შეიძლება უფლება მისცეს აპს, წაშალოს ან შეცვალოს ბრაუზერის მონაცემები. შენიშვნა: ეს ნებართვა არ შეიძლება შესრულდეს მესამე მხარის ბრაუზერების ან ვებ დათვალიერების შესაძლებლობის მქონე სხვა აპლიკაციების მიერ."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"მაღვიძარას დაყენება"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"აპს შეეძლება მაღვიძარას დაყენება დაინსტალირებული მაღვიძარას აპლიკაციაში. ამ ფუნქციას მაღვიძარას ზოგიერთი აპი არ იყენებს."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"ხმოვანი ფოსტის ჩათვლით"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"ნებას რთავს ამ აპს, შეცვალოს და ამოშალოს შეტყობინებები თქვენი ხმოვანი ფოსტის შემოსულებიდან."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ხმოვანი ფოსტის დამატება"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"აპს შეეძლება დაამატოს შეტყობინებები თქვენი ხმოვანი ფოსტის შემოსულებში."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"ხმოვანი ფოსტის წაკითხვა"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"აპს ეძლევა მთელი თქვენი ხმოვანი ფოსტების წაკითხვის უფლება."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ბრაუზერის გეოლოკაციის უფლებების შეცვლა"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"აპს შეეძლება ბრაუზერის გეოლოკაციის უფლებების შეცვლა. მავნე აპებმა ეს შესაძლოა გამოიყენონ ნებისმიერი ვებსაიტისთვის მდებარეობის შესახებ ინფორმაციის გასაგზავნად."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"პაკეტების გადამოწმება"</string>
@@ -1295,7 +1295,7 @@
<string name="sim_removed_message" msgid="5450336489923274918">"ფიჭური კავშირი არ იქნება ხელმისაწვდომი, ვიდრე არ ჩადებთ ქმედით SIM ბარათს და გადატვირთავთ."</string>
<string name="sim_done_button" msgid="827949989369963775">"დასრულდა"</string>
<string name="sim_added_title" msgid="3719670512889674693">"SIM ბარათი დაემატა"</string>
- <string name="sim_added_message" msgid="7797975656153714319">"გადატვირთთ თქვენი მოწყობილობა ფიჭურ ქსელზე წვდომისთვის."</string>
+ <string name="sim_added_message" msgid="7797975656153714319">"გადატვირთეთ თქვენი მოწყობილობა ფიჭურ ქსელზე წვდომისთვის."</string>
<string name="sim_restart_button" msgid="4722407842815232347">"გადატვირთვა"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"დროის დაყენება"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"თარიღის დაყენება"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"საშუალებას აძლევს აპლიკაციას მიემაგროს სანდო აგენტის სერვისს."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"განახლებასთან და აღდგენის სისტემასთან ინტერაქცია"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"საშუალებას აძლევს აპლიკაციას მოახდინოს აღდგენის სისტემასთან და სისტემის განახლებასთან ინტერაქცია."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"მედია პროეცირების სესიების შექმნა"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"საშუალებას აძლევს აპლიკაციას, შექმნას მედია პროეცირების სესიები. ამ სესიებმა შესაძლოა მისცეს აპლიკაციებს საშუალება, აღიბეჭდოს ეკრანისა და აუდიოს კონტენტი. ჩვეულებრივ აპს წესით ეს არასოდეს არ უნდა დასჭირდეს."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"მასშტაბის მართვისთვის შეეხეთ ორჯერ."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ვერ დაემატა ვიჯეტი."</string>
<string name="ime_action_go" msgid="8320845651737369027">"გადასვლა"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"რედაქტირება"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"ინტერნეტის გამოყენების გაფრთხილება"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"შეეხეთ მოხმარებისა და პარამეტრების სანახავად."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G-3G მონაც. გადაცემა გამორთულია"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G მონაც. გადაცემა გამორთულია"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"ფიჭური ინტერნეტი გამორთულია"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi მონაც. გადაცემა გამორთულია"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"ლიმიტი მიღწეულია"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"გადაჭარბებულია 2G-3G მონაცემების ლიმიტი"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4G ლიმიტი გადაჭარბებულია"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"ფიჭ. ინტერნეტის ლიმიტი მიღწეულია"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Wi‑Fi მონაცემთა ლიმიტი გადაჭარბებულია"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"ლიმიტი გადაჭარბებულია <xliff:g id="SIZE">%s</xliff:g>-ით."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"მონაცემთა ფონური გადაცემა შეზღუდულია"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"სისტემა"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth აუდიო"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"უსადენო ეკრანი"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"მედია გამომავალი"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"მოწყობილობასთან დაკავშირება"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"ეკრანის მოწყობილობაზე გადაცემა"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"მოწყობილობების ძიება…"</string>
@@ -1752,25 +1745,16 @@
<string name="item_is_selected" msgid="949687401682476608">"არჩეულია <xliff:g id="ITEM">%1$s</xliff:g>"</string>
<string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> წაიშალა"</string>
<string name="managed_profile_label_badge" msgid="2355652472854327647">"სამსახური <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for lock_to_app_toast (2126866321272822564) -->
- <skip />
+ <string name="lock_to_app_toast" msgid="2126866321272822564">"თქვენ იმყოფებით აპის ფიქსაციის რეჟიმში. გამოსასვლელად, დააჭირეთ და დააყოვნეთ ბოლო აპების ღილაკზე"</string>
<string name="lock_to_app_toast_locked" msgid="4229650395479263497">"თქვენ იმყოფებით აპის ფიქსაციის რეჟიმში."</string>
<string name="lock_to_app_title" msgid="5895142291937470019">"გსურთ „აპში ჩაკეტვის“ გამოყენება?"</string>
- <!-- no translation found for lock_to_app_description (2800403592608529611) -->
- <skip />
- <!-- no translation found for lock_to_app_negative (2259143719362732728) -->
- <skip />
+ <string name="lock_to_app_description" msgid="2800403592608529611">"„აპში ჩაკეტვა“ კეტავს ეკრანს ერთ აპში.\n\nგასასვლელად დააჭირეთ და არ აუშვათ ღილაკს „ბოლო აპები“."</string>
+ <string name="lock_to_app_negative" msgid="2259143719362732728">"არა, გმადლობთ"</string>
<string name="lock_to_app_positive" msgid="7085139175671313864">"დაწყება"</string>
- <!-- no translation found for lock_to_app_start (3074665051586318340) -->
- <skip />
- <!-- no translation found for lock_to_app_exit (8967089657201849300) -->
- <skip />
- <!-- no translation found for lock_to_app_use_screen_lock (1434584309048590886) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_pin (7908385370846820001) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_pattern (7763071104790758405) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_password (795224196583495868) -->
- <skip />
+ <string name="lock_to_app_start" msgid="3074665051586318340">"აპზე დაფიქსირებული"</string>
+ <string name="lock_to_app_exit" msgid="8967089657201849300">"აპზე დაფიქსირებული აღარ არის"</string>
+ <string name="lock_to_app_use_screen_lock" msgid="1434584309048590886">"გასვლამდე %1$s-ის მოთხოვნა"</string>
+ <string name="lock_to_app_unlock_pin" msgid="7908385370846820001">"PIN-კოდი"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="7763071104790758405">"განბლოკვის ნიმუში"</string>
+ <string name="lock_to_app_unlock_password" msgid="795224196583495868">"პაროლი"</string>
</resources>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index dd24dcf..172e45a 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Қолданбаға Bluetooth MAP хабарларын алуға және өңдеуге рұқсат береді. Бұл қолданба құрылғыңызға жіберілген хабарларды сізге көрсетпестен бақылауы немесе жоюы мүмкін екенін білдіреді."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"жұмыс істеп жатқан қолданбаларды шығарып алу"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Қолданбаларға ағымдағы және соңғы тапсырмалар туралы ақпарат алу мүмкіндігін береді. Бұл қолданбаға құрылғы қолданатын басқа қолданбалар туралы деректері анықтау мүмкіндігін беруі ықтимал."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"барлық пайдаланушылармен қарым-қатынас жасау"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Қолданбаға құрылғыдағы әртүрлі пайдаланушылар арасында әрекеттер орындау мүмкіндігін береді. Залалды қолданбалар бұны пайдаланушылар арасындағы қорғанысты бұзу үшін пайдалануы мүмкін."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"барлық пайдаланушылармен қарым-қатынас жасау лицензиясы"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Қолданбаларға телефонда сақталған Браузер кіріп шыққан барлық URL тарихын және Браузер бетбелгілерін өзгерту мүмкіндігін береді. Есіңізде болсын: бұл рұқсатты үшінші жақ браузерлері немесе веб шолу қабілеті бар басқа қолданбалар қолданбауы мүмкін."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"дабылды орнату"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Қолданбаға орнатылған оятқыш қолданбасында дабылды орнатуға рұқсат береді. Кейбір қолданбаларда бұл мүмкіндік іске асырылмауы мүмкін."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"дауыстық хабарлар жазу"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Қолданбаға дауыстық поштаңыздың «Кіріс» қалтасындағы хабарларды өзгертуге және жоюға рұқсат етеді."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"дауыс хабарын қосу"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Қолданбаға дауыстық поштаңыздың «Кіріс» қалтасына хабарлар қосуға рұқсат береді."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"дауыстық поштаны оқу"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Қолданбаға дауыстық хабарларыңызды оқуға рұқсат етеді."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"браузердің геолокация рұқсаттарын өзгерту"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Қолданбаға браузердің геолокация рұқсаттарын өзгертуге рұқсат береді. Зиянкес қолданбалар мұны кездейсоқ веб-сайттарға орын туралы ақпаратты жіберуге рұқсат беру үшін пайдалануы мүмкін."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"жинақтарды растау"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Қолданбаға сенімді агент қызметіне байластыруға рұқсат береді."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Жаңарту және қалпына келтіру жүйелерімен қатынасу"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Қолданбаның қалпына келтіру жүйесімен және жүйе жаңартуларымен қатынасу мүмкіндігін береді."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Тасушыны проекциялау сеанстарын жасау"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Қолданбаға тасушыны проекциялау сеанстарын жасауға рұқсат етеді. Бұл сеанстар қолданбаларға дисплей және аудио мазмұнын түсіру мүмкіндігін қамтамасыз етеді. Қалыпты қолданбалар үшін ешқашан қажет болмайды."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Масштабтауды басқару үшін екі рет түртіңіз"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Виджетті қосу."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Өту"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Өзгерту"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Дерекқор қолдануға қатысты ескерту"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Қолданыс және параметрлерді көру үшін түртіңіз."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G-3G деректері өшірулі"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G деректері өшірулі"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Ұялы деректер өшірулі"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi деректері өшірулі"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Шекке жеттіңіз"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2Г-3Г дерекқор шектеуінен асып кетті"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4Ш дерекқор шектеуінен асып кетті"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Ұялы деректер шегі асырылды"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Wi-Fi дерекқор шектеуінен асып кетті"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"Анықталған уақтыттан <xliff:g id="SIZE">%s</xliff:g> асты."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Тарихы туралы дерекқор шектелген"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Жүйе"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth aудио"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Сымсыз дисплей"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Meдиа шығысы"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Құрылғыға жалғау"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Экранды құрылғымен байланыстыру"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Құрылғыларды іздеуде…"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 7cbaca8..7b2c26c 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"ឲ្យកម្មវិធីទទួល និងដំណើរការសារ MAP ប៊្លូធូស។ មានន័យថា កម្មវិធីអាចតាមដាន ឬលុបសារដែលបានផ្ញើទៅឧបករណ៍របស់អ្នកដោយមិនបង្ហាញពួកវាដល់អ្នក។"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ទៅយកកម្មវិធីកំពុងដំណើរការ"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"ឲ្យកម្មវិធីទៅយកព័ត៌មានលម្អិតអំពីកិច្ចការដែលកំពុងដំណើរការបច្ចុប្បន្ន។ វាអាចឲ្យកម្មវិធីរកមើលព័ត៌មានថាតើកម្មវិធីណាមួយត្រូវបានប្រើលើឧបករណ៍។"</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"អន្តរកម្មតាមអ្នកប្រើ"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"ឲ្យកម្មវិធីអនុវត្តសកម្មភាពឆ្លងអ្នកប្រើផ្សេងៗលើឧបករណ៍។ កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីបំពានការការពាររវាងអ្នកប្រើ។"</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"អាជ្ញាប័ណ្ណពេញលេញ ដើម្បីទាក់ទងអ្នកប្រើ"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"ឲ្យកម្មវិធីកែប្រវត្តិ ឬចំណាំរបស់កម្មវិធីអ៊ីនធឺណិតដែលបានរក្សាទុកក្នុងទូរស័ព្ទរបស់អ្នក។ កម្មវិធីព្យាបាទអាចប្រើវាដើម្បីលុប ឬកែទិន្នន័យនៃកម្មវិធីអ៊ីនធឺណិតរបស់អ្នក។ ចំណាំ៖ សិទ្ធិនេះអាចត្រូវបានបង្ខំដោយកម្មវិធីអ៊ីនធឺណិតភាគីទីបី ឬកម្មវិធីផ្សេងដែលមានសមត្ថភាពរុករកបណ្ដាញ។ស"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"កំណត់សំឡេងរោទ៍"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"ឲ្យកម្មវិធីកំណត់សំឡេងរោទ៍ក្នុងកម្មវិធីនាឡិការោទ៍បានដំឡើង។ កម្មវិធីនាឡិការោទ៍មួយចំនួនអាចមិនអនុវត្តលក្ខណៈនេះ។"</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"សរសេរសារជាសំឡេង"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"ឲ្យកម្មវិធីកែប្រែ និងលុបសារចេញពីប្រអប់ទទួលសារជាសំឡេងរបស់អ្នក។"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"បន្ថែមសារជាសំឡេង"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"ឲ្យកម្មវិធីបន្ថែមសារទៅប្រអប់ទទួលសារជាសំឡេងរបស់អ្នក។"</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"អានសារជាសំឡេង"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"ឲ្យកម្មវិធីអានសារជាសំឡេងរបស់អ្នក។"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"កែសិទ្ធិទីតាំងភូមិសាស្ត្ររបស់កម្មវិធីអ៊ីនធឺណិត"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ឲ្យកម្មវិធីកែសិទ្ធិទីតាំងភូមិសាស្ត្ររបស់កម្មវិធីអ៊ីនធឺណិត។ កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីឲ្យផ្ញើព័ត៌មានទីតាំងទៅតំបន់បណ្ដាញដោយបំពាន។"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ផ្ទៀងផ្ទាត់កញ្ចប់"</string>
@@ -1392,10 +1392,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"ឲ្យកម្មវិធីភ្ជាប់សេវាកម្មភ្នាក់ងារដែលទុកចិត្ត។"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"អន្តរកម្មជាមួយបច្ចុប្បន្នភាព និងប្រព័ន្ធសង្គ្រោះ"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"ឲ្យកម្មវិធីមានអន្តរកម្មជាមួយប្រព័ន្ធសង្គ្រោះ និងបច្ចុប្បន្នភាពប្រព័ន្ធ។"</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"បង្កើតសម័យគម្រោងមេឌៀ"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"ឲ្យកម្មវិធីបង្កើតសម័យគម្រោងមេឌៀ។ សម័យទាំងនេះអាចធ្វើឲ្យកម្មវិធីមានលទ្ធភាពចាប់យកមាតិកានៃការបង្ហាញ និងសំឡេង។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"ប៉ះពីរដង ដើម្បីគ្រប់គ្រងការពង្រីក"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"មិនអាចបន្ថែមធាតុក្រាហ្វិក។"</string>
<string name="ime_action_go" msgid="8320845651737369027">"ទៅ"</string>
@@ -1520,20 +1518,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"ការព្រមានប្រើទិន្នន័យ"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"ប៉ះ ដើម្បីមើលការប្រើ និងការកំណត់។"</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"ទិន្នន័យ 2G-3G បានបិទ"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"ទិន្នន័យ 4G បានបិទ"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"ទិន្នន័យចល័តបានបិទ"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"ទិន្នន័យវ៉ាយហ្វាយបានបិទ"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"បានដល់ដែនកំណត់"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"លើសដែនកំណត់ទិន្នន័យ 2G-3G"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"បានលើសដែនកំណត់ទិន្នន័យ 4G"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"បានលើសដែនកំណត់ទិន្នន័យចល័ត"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"លើសដែនកំណត់ទិន្នន័យវ៉ាយហ្វាយ"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> លើដែនកំណត់បានបញ្ជាក់។"</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"បានដាក់កម្រិតទិន្នន័យផ្ទៃខាងក្រោយ"</string>
@@ -1569,7 +1561,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ប្រព័ន្ធ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"សំឡេងប៊្លូធូស"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"បង្ហាញបណ្ដាញឥតខ្សែ"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"លទ្ធផលមេឌៀ"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"ភ្ជាប់ឧបករណ៍"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"ចាត់ថ្នាក់អេក្រង់ទៅឧបករណ៍"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"កំពុងស្វែងរកឧបករណ៍..."</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index c9e0519..cedab61 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"ಬ್ಲೂಟೂಟ್ MAP ಸಂದೇಶಗಳನ್ನು ಸ್ವೀಕರಿಸಲು ಮತ್ತು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಇದರರ್ಥ, ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಕಳುಹಿಸಲಾಗಿರುವ ಸಂದೇಶಗಳನ್ನು ನಿಮಗೆ ತೋರಿಸದೆಯೇ, ಅಪ್ಲಿಕೇಶನ್ ಅವುಗಳನ್ನು ಮಾನಿಟರ್ ಮಾಡಬಹುದು ಅಥವಾ ಅಳಿಸಬಹುದು."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ರನ್ ಆಗುತ್ತಿರುವ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಹಿಂಪಡೆಯಿರಿ"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"ಪ್ರಸ್ತುತವಿರುವ ಮತ್ತು ಇತ್ತೀಚಿಗೆ ಚಾಲ್ತಿಯಾಗಿರುವ ಕಾರ್ಯಗಳ ಕುರಿತ ಮಾಹಿತಿಯನ್ನು ಮರುಪಡೆದುಕೊಳ್ಳಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಇದು ಸಾಧನದಲ್ಲಿ ಯಾವ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಬಳಸಲಾಗಿದೆ ಎಂಬುದರ ಕುರಿತ ಮಾಹಿತಿಯನ್ನು ಅನ್ವೇಷಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸಬಹುದು."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"ಬಳಕೆದಾರರ ಜೊತೆ ಸಂವಹನ ನಡೆಸಿ"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"ಸಾಧನದಲ್ಲಿರುವ ವಿವಿಧ ಬಳಕೆದಾರರಾದ್ಯಂತ ಕ್ರಿಯೆಗಳನ್ನು ನಿರ್ವಹಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ದುರುದ್ದೇಶಪೂರಿತ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಇದನ್ನು ಬಳಕೆದಾರರ ನಡುವಿನ ರಕ್ಷಣೆಯನ್ನು ಉಲ್ಲಂಘಿಸಲು ಬಳಸಿಕೊಳ್ಳಬಹುದು."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"ಬಳಕೆದಾರರ ಜೊತೆಗೆ ಸಂವಹನ ನಡೆಸಲು ಪೂರ್ಣ ಪರವಾನಗಿ"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿ ಸಂಗ್ರಹಿಸಲಾಗಿರುವ ಬ್ರೌಸರ್ನ ಇತಿಹಾಸ ಅಥವಾ ಬುಕ್ಮಾರ್ಕ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಇದು ಬ್ರೌಸರ್ನ ಡೇಟಾವನ್ನು ಅಳಿಸಲು ಅಥವಾ ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಕಲ್ಪಿಸಿಕೊಡಬಹುದು. ಗಮನಿಸಿ: ಈ ಅನುಮತಿಯನ್ನು ವೆಬ್ ಬ್ರೌಸಿಂಗ್ ಸಾಮರ್ಥ್ಯಗಳನ್ನು ಹೊಂದಿರುವ ಮೂರನೇ-ವ್ಯಕ್ತಿ ಬ್ರೌಸರ್ಗಳು ಅಥವಾ ಅಪ್ಲಿಕೇಶನ್ಗಳ ಮೂಲಕ ಜಾರಿಗೊಳಿಸಲಾಗುವುದಿಲ್ಲ."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"ಅಲಾರಮ್ ಹೊಂದಿಸಿ"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"ಸ್ಥಾಪಿಸಲಾದ ಅಲಾರಾಂ ಗಡಿಯಾರ ಅಪ್ಲಿಕೇಶನ್ನಲ್ಲಿ ಅಲಾರಾಂ ಹೊಂದಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಕೆಲವು ಅಲಾರಾಂ ಗಡಿಯಾರ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಈ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸದಿರಬಹುದು."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"ಧ್ವನಿಮೇಲ್ಗಳನ್ನು ಬರೆಯಿರಿ"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"ನಿಮ್ಮ ಧ್ವನಿಮೇಲ್ ಇನ್ಬಾಕ್ಸ್ನಿಂದ ಸಂದೇಶಗಳನ್ನು ತೆಗೆದುಹಾಕಲು ಮತ್ತು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ಧ್ವನಿಮೇಲ್ ಸೇರಿಸಿ"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"ನಿಮ್ಮ ದ್ವನಿಮೇಲ್ ಇನ್ಬಾಕ್ಸ್ಗೆ ಸಂದೇಶಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"ಧ್ವನಿಮೇಲ್ ಓದಿ"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"ನಿಮ್ಮ ಎಲ್ಲಾ ಧ್ವನಿಮೇಲ್ಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ಬ್ರೌಸರ್ ಜಿಯೋಲೊಕೇಶನ್ ಅನುಮತಿಗಳನ್ನು ಮಾರ್ಪಡಿಸಿ"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ಬ್ರೌಸರ್ನ ಜಿಯೋಲೊಕೇಶನ್ ಅನುಮತಿಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ದುರುದ್ದೇಶಪೂರಿತ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಅನಿರ್ಬಂಧಿತ ವೆಬ್ ಸೈಟ್ಗಳಿಗೆ ಸ್ಥಳ ಮಾಹಿತಿ ಕಳುಹಿಸುವುದನ್ನು ಅನುಮತಿಸಲು ಇದನ್ನು ಬಳಸಬಹುದು."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ಪ್ಯಾಕೇಜ್ಗಳನ್ನು ಪರಿಶೀಲಿಸಿ"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ಸಿಸ್ಟಂ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ಬ್ಲೂಟೂತ್ ಆಡಿಯೊ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ವಯರ್ಲೆಸ್ ಪ್ರದರ್ಶನ"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"ಮೀಡಿಯಾ ಔಟ್ಪುಟ್"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"ಸಾಧನಕ್ಕೆ ಸಂಪರ್ಕಪಡಿಸಿ"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"ಸಾಧನಕ್ಕೆ ಬಿತ್ತರಿಸುವ ಪರದೆ"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"ಸಾಧನಗಳನ್ನು ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 1afab473..8154700 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"앱이 블루투스 MAP 메시지를 수신하고 처리하도록 허용합니다. 이는 앱이 나에게 보여주지 않고 내 기기에 전송된 메시지를 모니터링하거나 삭제할 수 있음을 의미합니다."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"실행 중인 앱 검색"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"앱이 현재 실행 중이거나 최근에 실행된 작업에 대한 정보를 검색할 수 있도록 허용합니다. 이 경우 앱이 기기에서 사용되는 다른 앱에 대한 정보를 검색할 수 있습니다."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"여러 사용자와의 상호작용"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"앱이 기기에서 다양한 사용자에 대한 작업을 수행할 수 있도록 허용합니다. 이 경우 악성 앱이 사용자 간의 보호를 위반할 수 있습니다."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"여러 사용자와의 상호작용을 위한 정식 라이선스"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"앱이 휴대전화에 저장된 브라우저 기록 또는 북마크를 수정할 수 있도록 허용합니다. 이 경우 앱이 브라우저 데이터를 삭제 또는 수정할 수 있습니다. 참고: 이 권한은 타사 브라우저 또는 브라우저 기능을 가진 기타 애플리케이션에 적용되지 않습니다."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"알람 설정"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"앱이 설치된 알람 시계 앱에서 알람을 설정할 수 있도록 허용합니다. 일부 알람 시계 앱에는 이 기능이 구현되지 않을 수 있습니다."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"음성사서함 쓰기"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"앱이 음성사서함에서 메시지를 수정하고 삭제하도록 허용합니다."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"음성사서함 추가"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"앱이 음성사서함에 메시지를 추가할 수 있도록 허용합니다."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"음성사서함 읽기"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"앱이 음성사서함을 읽도록 허용합니다."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"브라우저 위치 정보 권한 수정"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"앱이 브라우저의 위치 정보 권한을 수정할 수 있도록 허용합니다. 이 경우 악성 앱이 이 기능을 이용하여 임의의 웹사이트에 위치 정보를 보낼 수 있습니다."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"패키지 확인"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"애플리케이션이 Trust Agent 서비스에 바인딩할 수 있도록 허용합니다."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"업데이트 및 복구 시스템과 상호작용"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"애플리케이션이 복구 시스템 및 시스템 업데이트와 상호작용할 수 있도록 허용합니다."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"미디어 프로젝션 세션 만들기"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"애플리케이션이 미디어 프로젝션 세션을 만드는 것을 허용합니다. 이 세션은 애플리케이션에 디스플레이 및 오디오 콘텐츠를 캡처하는 기능을 제공할 수 있습니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"확대/축소하려면 두 번 터치하세요."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"위젯을 추가할 수 없습니다."</string>
<string name="ime_action_go" msgid="8320845651737369027">"이동"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"수정"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"데이터 사용 경고"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"사용량 및 설정을 보려면 터치하세요."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G-3G 데이터 사용 중지됨"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G 데이터 사용 중지됨"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"이동통신 데이터 사용 중지됨"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi 데이터 사용 중지됨"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"한도 도달"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G - 3G 데이터 제한 초과됨"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4GB의 데이터 제한 초과됨"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"이동통신 데이터 한도 초과"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Wi-Fi 데이터 한도 초과됨"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> - 지정된 한도 초과"</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"백그라운드 데이터 사용이 제한됨"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"시스템"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"블루투스 오디오"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"무선 디스플레이"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"미디어 출력"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"기기에 연결"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"기기로 화면 전송"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"기기 검색 중…"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 2ab9816..7a1c96b 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -430,6 +430,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Колдонмого Bluetooth MAP билдирүүлөрүн алуу жана иштеп чыгуу мүмкүнчүлүгүн берет. Демек, колдонмо түзмөгүңүздөгү билдирүүлөрдү сизге көрсөтпөстөн тескеп же жок кыла алат."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"иштеп жаткан колдонмолорду түшүрүп алуу"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Колдонмого учурдагы жана акыркы убакытта пайдаланылган колдонмолор тууралуу маалымат алууга уруксат берет. Бул колдонмого түзмөктө кандай колдонмолор колдонулаарын билип алууга жол бериши мүмкүн."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"колдонуучулар менен иштешүү"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Колдонмого түзмөктөгү ар кандай колдонуучулардын атынан кыймыл-аракеттерди жүргүзүү уруксатын берет. Зыяндуу колдонмолор, муну менен колдонуучулар аарсындагы коргонду бузуп салышы мүмкүн."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"колдонуучулар менен иштешүү үчүн толук лицензия"</string>
@@ -1340,16 +1344,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Колдонмого телефонуңузда сакталган Серепчинин тарыхын жана Серепчинин бүктөмөлөрүн өзгөртүү уруксатын берет. Бул колдонмого Серепчинин берилиштерин өчүрүү же өзгөртүү уруксатын берет. Эскертүү: бул уруксат үчүнчү тараптык интернет-серепчилерге, же интернетке кирүү мүмкүнчүлүгү бар колдонмолорго таасир этпеши мүмкүн."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"ойготкуч коюу"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Колдонмого ойготкуч саат колдонмосуна үн ишаратын коюу мүмкүнчүлүгүн берет. Айрым ойготкуч саат колдонмолорунда бул мүмкүнчүлүк иштебеши мүмкүн."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"үн почталарын жазуу"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Колдонмонун үн почтаңыздын кирүү кутусунан билдирүүлөрдү өзгөртүп жана алып салуусуна жол ачат."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"үнкат кошуу"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Колдонмого үн почтаңыздын кирүү кутусуна билдирүүлөрдү кошуу мүмкүнчүлүгүн берет."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"үн почтасын окуу"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Колдонмонун үн почталарыңызды окуусуна жол ачат."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Серепчинин гео-жайгашуу уруксаттарын өзгөртүү"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Колдонмого Серепчинин гео-жайгашуу уруксаттарын өзгөртүү мүмкүнчүлүгүн берет. Кесепеттүү колдонмолор ушундай мүмкүнчүлүктөн пайдаланып ар кайсы вебсайтка жайгашкан жер жөнүндө маалыматтын жөнөтүлүшүнө уруксат берип салышы мүмкүн."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"таңгактарды текшерүү"</string>
@@ -1809,10 +1809,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Колдонмого ишенимдүү агент кызматына жалгашууга мүмкүнчүлүк берет."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Калыбына келтирүү системасы жана жаңыртуулар менен иштөө"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Колдонмого калыбына келтирүү системасы жана система жаңыртуулары менен карым-катнашуу мүмкүнчүлүгүн берет."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Медиа чагылдыруу сеанстарын түзүү"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Колдонмого медиа чагылдыруу сеанстарын түзүү мүмкүнчүлүгүн берет. Мындай сеанстардын жардамы менен, колдонмолор дисплей жана видео мазмунду сүрөткө тартып турушат. Кадимки колдонмолор үчүн талап кылынбайт."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Чен өлчөмүн көзөмөлдөө үчүн эки жолу тийип коюңуз"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Виджетти кошуу мүмкүн болбоду."</string>
<!-- no translation found for ime_action_go (8320845651737369027) -->
@@ -1986,20 +1984,14 @@
<!-- no translation found for data_usage_warning_title (1955638862122232342) -->
<skip />
<string name="data_usage_warning_body" msgid="2814673551471969954">"Колдонууну көрүш үчүн басыңыз."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2Гб-3Гб көлмдөгү дайындр өчүрлдү."</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4Гб көлөмдөгү дайындар өчүрүлдү"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Уюктук дайындар тармагы өчүк"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi дайындар тармагы өчүк"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Белгиленген чекке жеттиңиз"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G-3G трафик чектен ашты"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4G трафик чектен ашты"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Уюкт дайндр белглнгн чегнн аштңз"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Wi-Fi трафик чегинен ашты"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"Орнотулган чектөөдөн <xliff:g id="SIZE">%s</xliff:g> ашты."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Фондук трафик чектелген"</string>
@@ -2049,7 +2041,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Систем"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth аудио"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Зымсыз дисплей"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Медиа чыгаруу"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Түзмөккө туташуу"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Сырткы экранга чыгаруу"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Түзмөктөр изделүүдө..."</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 22404ff..7b9c218 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"ອະນຸຍາດໃຫ້ແອັບຯສາມາດຮັບແລະສົ່ງຂໍ້ຄວາມ Bluetooth MAP ໄດ້. ນີ້ໝາຍຄວາມວ່າແອັບຯຈະສາມາດກວດເບິ່ງ ຫຼືລຶບຂໍ້ຄວາມທີ່ສົ່ງຫາອຸປະກອນຂອງທ່ານໄດ້ໂດຍບໍ່ສະແດງຂໍ້ຄວາມເຫຼົ່ານັ້ນໃຫ້ທ່ານເຫັນ."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ດຶງແອັບຯທີ່ເຮັດວຽກຢູ່ມາ"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"ອະນຸຍາດໃຫ້ແອັບຯດຶງຂໍ້ມູນກ່ຽວກັບການເຮັດວຽກໃນປັດຈຸບັນ ແລະຫາກໍຜ່ານມາ. ແອັບຯທີ່ເປັນອັນຕະລາຍອາດຄົ້ນພົບຂໍ້ມູນ ກ່ຽວກັບແອັບພລິເຄຊັນທີ່ໃຊ້ຢູ່ໃນອຸປະກອນໄດ້."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"ການຕອບໂຕ້ລະຫວ່າງຜູ່ໃຊ້"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"ອະນຸຍາດໃຫ້ແອັບຯດຳເນີນການ ສັ່ງງານຜ່ານຜູ່ໃຊ້ອື່ນໆໃນອຸປະກອນ. ແອັບຯທີ່ເປັນອັນຕະລາຍອາດໃຊ້ຄວາມສາມາດນີ້ ເພື່ອລະເມີດການປ້ອງກັນລະຫວ່າງຜູ່ໃຊ້."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"ໃບອະນຸຍາດສະບັບເຕັມໃນການໂຕ້ຕອບລະຫວ່າງຜູ່ໃຊ້"</string>
@@ -1555,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ລະບົບ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ສຽງ Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ການສະແດງຜົນໄຮ້ສາຍ"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"ມີເດຍເອົ້າພຸດ"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"ເຊື່ອມຕໍ່ຫາອຸປະກອນ"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"ສົ່ງພາບໜ້າຈໍໄປຫາອຸປະກອນ"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"ກຳລັງຊອກຫາອຸປະກອນ..."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index cdc2b47..61dcf86 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Programai leidžiama gauti ir apdoroti „Bluetooth“ MAP pranešimus. Tai reiškia, kad programa gali stebėti ir ištrinti į jūsų įrenginį siunčiamus pranešimus jums jų neparodžiusi."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"nuskaityti vykdomas programas"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Leidžiama programai nuskaityti informaciją apie šiuo ir pastaruoju metu vykdomas užduotis. Taip programa gali atrasti informacijos, kokios programos naudojamos įrenginyje."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"sąveikauti su naudotojais"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Leidžiama programai atlikti veiksmus skirtingų įrenginio naudotojų profiliuose. Kenkėjiškos programos gali pasinaudoti šiuo leidimu, kad pažeistų naudotojų saugumą."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"visa licencija, leidžianti sąveikauti su naudotojais"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Leidžiama programai keisti naršyklės istoriją ar žymes, išsaugotas telefone. Dėl to programai gali būti leidžiama ištrinti ar keisti naršyklės duomenis. Pastaba: šis leidimas nesuteikiamas trečiosios šalies naršyklėms ar kitoms programoms, kuriomis galima naršyti žiniatinklį."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"nustatyti pavojaus signalą"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Leidžiama programai nustatyti signalą įdiegtoje žadintuvo programoje. Kai kuriose žadintuvo programose ši funkcija gali nebūti nevykdoma."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"rašyti balso pašto pranešimus"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Programai leidžiama keisti ir šalinti pranešimus iš balso pašto gautųjų."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"pridėti balso pašto pranešimų"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Leidžia programai pridėti pranešimų prie jūsų balso pašto gautųjų."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"skaityti balso pašto pranešimus"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Programai leidžiama skaityti balso pašto pranešimus."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"keisti naršyklės geografinės vietos leidimus"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Leidžiama programai keisti naršyklės geografinės vietos leidimus. Kenkėjiškos programos gali tai naudoti, kad leistų siųsti vietos informaciją abejotinoms svetainėms."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"patikrinti paketus"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Programai leidžiama susisaistyti su „trust agent“ paslauga."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Sąveikauti su naujiniu ir atkūrimo sistema"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Programai leidžiama sąveikauti su atkūrimo sistema ir sistemos naujiniais."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Kurti medijos projekcijų seansus"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Programai leidžiama kurti medijos projekcijų seansus. Sukūrusios šiuos seansus, programos gali fiksuoti vaizdo ir garso turinį. To niekada neturėtų prireikti naudojant įprastas programas."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Dukart palieskite, kad valdytumėte mastelio keitimą"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nepavyko pridėti."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Pradėti"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Redaguoti"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Įspėjimas dėl duomenų naudojimo"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Palieskite ir žr. naud. ir nust."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G–3G duomenys išjungti"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G duomenys išjungti"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Mobil. ryšio duomenys išjungti"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"„Wi-Fi“ duomenys išjungti"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Pasiektas limitas"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Viršyta 2G–3G duomenų riba"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Viršyta 4G duomenų riba"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Viršytas mobil. duom. limitas"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Viršytas „Wi-Fi“ duomenų aprib."</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> viršyta nurodyta riba."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Apriboti foniniai duomenys"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"„Bluetooth“ garsas"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Belaidis rodymas"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Medijos išvestis"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Prijungimas prie įrenginio"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Perduoti ekraną į įrenginį"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Ieškoma įrenginių…"</string>
@@ -1755,7 +1748,7 @@
<string name="lock_to_app_toast" msgid="2126866321272822564">"Įjungėte programos užrakinimo funkcijos režimą. Kad išeitumėte, palieskite ir palaikykite mygtuką „Naujausios“"</string>
<string name="lock_to_app_toast_locked" msgid="4229650395479263497">"Įjungėte programos užrakinimo funkcijos režimą."</string>
<string name="lock_to_app_title" msgid="5895142291937470019">"Naudoti programos užrakinimo funkciją?"</string>
- <string name="lock_to_app_description" msgid="2800403592608529611">"Naudojant programos užrakinimo funkciją ekrane užrakinama viena programa.\n\nKad išeitumėte, palieskite ir palaikykite mygtuką „Naujausios“"</string>
+ <string name="lock_to_app_description" msgid="2800403592608529611">"Naudojant programos užrakinimo funkciją ekrane užrakinama viena programa.\n\nKad išeitumėte, palieskite ir palaikykite mygtuką „Naujausios“."</string>
<string name="lock_to_app_negative" msgid="2259143719362732728">"NE, AČIŪ"</string>
<string name="lock_to_app_positive" msgid="7085139175671313864">"ĮJUNGTI"</string>
<string name="lock_to_app_start" msgid="3074665051586318340">"Naudojama programos užrakinimo funkcija"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 66a4fad..e4cd1fe 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Ļauj lietotnei saņemt un apstrādāt Bluetooth MAP ziņojumus. Tas nozīmē, ka lietotne var pārraudzīt vai dzēst uz jūsu ierīci nosūtītos ziņojumus, neparādot tos jums."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"izgūt izmantotās lietotnes"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Ļauj lietotnei izgūt informāciju par pašreiz un nesen darbinātajiem uzdevumiem. Tādējādi lietotne var atklāt informāciju par ierīcē izmantotajām lietojumprogrammām."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"darboties visos lietotāju kontos"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Ļauj lietotnei veikt darbības vairāku ierīces lietotāju kontos. Ļaunprātīgas lietotnes var izmantot šo atļauju, lai apdraudētu lietotāju kontu drošību."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"pilna licence ar atļauju darboties visos lietotāju kontos"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Ļauj lietotnei mainīt tālrunī saglabāto pārlūkprogrammas vēsturi vai grāmatzīmes. Tas var ļaut lietotnei dzēst vai pārveidot pārlūkprogrammas datus. Piezīme: šo atļauju nevar piemērot trešo pušu pārlūkprogrammas vai citas lietojumprogrammas ar tīmekļa pārlūkošanas iespējām."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"iestatīt modinātāju"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Ļauj lietotnei iestatīt signālu instalētajā modinātājpulksteņa lietotnē. Dažās modinātājpulksteņu lietotnēs šo funkciju, iespējams, nevar ieviest."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"rakstīt balss pasta ziņojumus"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Ļauj lietotnei rediģēt un noņemt ziņojumus no jūsu balss pasta iesūtnes."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"pievienot balss pastu"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Ļauj lietotnei pievienot ziņojumus jūsu balss pasta iesūtnei."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"lasīt balss pastu"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Ļauj lietotnei lasīt jūsu balss pasta ziņojumu."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"pārveidot pārlūkprogrammas ģeogrāfiskās atrašanās vietas atļaujas"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Ļauj lietotnei modificēt pārlūkprogrammas ģeogrāfiskās atrašanās vietas atļaujas. Ļaunprātīgas lietotnes to var izmantot, lai atļautu atrašanās vietas informācijas sūtīšanu uz citām vietnēm."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"pakotņu verificēšana"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Ļauj lietojumprogrammai izveidot savienojumu ar uzticamības pārbaudes pakalpojumu."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Mijiedarbošanās ar atjauninājumu un atkopšanas sistēmu"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Ļauj lietojumprogrammai mijiedarboties ar atkopšanas sistēmu un sistēmas atjauninājumiem."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Izveidot satura projekcijas sesijas"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Ļauj lietojumprogrammai izveidot satura projekcijas sesijas. Šīs sesijas var atļaut lietojumprogrammām uzņemt ekrāna un skaņas saturu. Parastām lietotnēm tas nekad nav nepieciešams."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Pieskarieties divreiz, lai kontrolētu tālummaiņu."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nevarēja pievienot logrīku."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Doties uz"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Rediģēt"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Datu izmantošanas brīdinājums"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Piesk., lai sk. lietoš. un iest."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G–3G dati ir atslēgti"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G dati ir atslēgti"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Mobilie dati ir atslēgti"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi dati ir atslēgti"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Sasniegts ierobežojums"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G-3G datu ierobež. pārsniegts"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4G datu limits pārsniegts"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Pārsniegts mob. datu ierobežojums"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Wi-Fi datu ierobež. pārsniegts"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> virs norādītā ierobežojuma."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Fona dati ir ierobežoti."</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistēma"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bezvadu attēlošana"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Multivides izeja"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Savienojuma izveide ar ierīci"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Ekrāna apraide uz ierīci"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Notiek ierīču meklēšana…"</string>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index 5636655..0e54fe5 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Овозможува апликацијата да прима и да обработува пораки МАП преку Bluetooth. Тоа значи дека апликацијата може да следи или да брише пораки испратени до вашиот уред без да ви ги прикаже вам."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"обнови активни апликации"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Овозможува апликацијата да поврати информации за тековно и до неодамна активни задачи. Ова може да овозможи апликацијата да открие информации за тоа кои апликации се користат на уредот."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"комуницирај со корисници"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Овозможува апликацијата да врши дејства кај различни корисници на уредот. Злонамерните апликации може да го искористат тоа да ја нарушат заштитата меѓу корисниците."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"целосна дозвола за комуникација меѓу корисници"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Овозможува апликацијата да ја менува историјата на прелистувачот или обележувачите зачувани во вашиот телефон. Ова може да овозможи апликацијата да избрише или да измени податоци за прелистувач. Напомена: оваа дозвола не може да ја наметнат прелистувачи на трети лица или други апликации со способности за прелистување на интернет."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"постави аларм"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Дозволува апликацијата да постави аларм во инсталираната апликација со будилник. Некои апликации со будилници може да не ја применуваат оваа можност."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"пишувај говорна пошта"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Ѝ дозволува на апликацијата да ги менува и да ги отстранува пораките од приемното сандаче за говорна пошта."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"додај говорна пошта"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Дозволува апликацијата да додава пораки во сандачето за гласовна пошта."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"читај говорна пошта"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Ѝ дозволува на апликацијата да ги чита говорните пораки."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"измени дозволи за геолокација на прелистувач"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Дозволува апликацијата да ги измени дозволите за геолокација на прелистувачот. Злонамерните апликации може да го искористат тоа за да се дозволи испраќање информации за локација до случајни веб-локации."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"потврди пакети"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Дозволува апликацијата да се поврзе со услуга за агенти за доверба."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Комуницирај со системот за обновување и ажурирање"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Овозможува апликацијата да комуницира со системот за обновување и ажурирање."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Создади сесии за проектирање на медиуми"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Дозволува апликацијата да создава сесии за проектирање на медиуми. Овие сесии им обезбедуваат можност на апликациите да снимаат екранска и аудиосодржина. Не би требало да се користи кај вообичаени апликации."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Допрете двапати за регулирање на зумирањето"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Не можеше да се додаде виџет."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Оди"</string>
@@ -1520,20 +1518,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Уреди"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Предупредување за користење податоци"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Допри за да видиш употреба и подесувања."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"Податоците 2G-3G се исклучени"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"Податоците 4G се исклучени"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Мобилните податоци се исклучени"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Подат. преку Wi-Fi се исклучени"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Го достигнавте лимитот"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Надминат лимит од 2G-3G податоци"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Надминат лимит од 4G податоци"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Лимитот за моб. подат. е надминат"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Надминат лимит на Wi-Fi податоци"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> над назначената граница."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Подат. од заднина се ограничени"</string>
@@ -1569,7 +1561,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Систем"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Аудио на Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Безжичен приказ"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Излез за медиуми"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Поврзи се со уред"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Префрли екран на уред"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Пребарување за уреди..."</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 4760788..f994247 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Bluetooth MAP സന്ദേശങ്ങൾ സ്വീകരിക്കുന്നതിനും പ്രോസസ്സുചെയ്യുന്നതിനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഉപകരണത്തിലേക്ക് അയച്ച സന്ദേശങ്ങൾ നിങ്ങളെ കാണിക്കാതെ തന്നെ നിരീക്ഷിക്കാനോ ഇല്ലാതാക്കാനോ അപ്ലിക്കേഷനാവും."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"പ്രവർത്തിക്കുന്ന അപ്ലിക്കേഷനുകൾ വീണ്ടെടുക്കുക"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"നിലവിലും സമീപകാലത്തും പ്രവർത്തിക്കുന്ന ടാസ്ക്കുകളെക്കുറിച്ചുള്ള വവിവരങ്ങൾ വീണ്ടെടുക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് ഉപകരണത്തിൽ ഉപയോഗിച്ച അപ്ലിക്കേഷനുകളെക്കുറിച്ചുള്ള വിവരം കണ്ടെത്താൻ അപ്ലിക്കേഷനെ അനുവദിക്കാനിടയുണ്ട്."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"എല്ലാ ഉപയോക്താക്കളുമായും സംവദിക്കുക"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"ഉപകരണത്തിലെ വ്യത്യസ്ത ഉപയോക്താക്കളിലുടനീളം പ്രവർത്തനങ്ങൾ നടത്താൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ക്ഷുദ്രകരമായ അപ്ലിക്കേഷനുകൾ ഉപയോക്താക്കൾക്കിടയിലുള്ള പരിരക്ഷ ലംഘിക്കാൻ ഇത് ഉപയോഗിക്കാനിടയുണ്ട്."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"എല്ലാ ഉപയോക്താക്കളുമായും സംവദിക്കാനുള്ള പൂർണ്ണ ലൈസൻസ്"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"നിങ്ങളുടെ ഫോണിൽ സംഭരിച്ചിരിക്കുന്ന ബ്രൗസറിന്റെ ചരിത്രമോ ബുക്ക്മാർക്കുകളോ പരിഷ്ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് ബ്രൗസർ ഡാറ്റ മായ്ക്കാനോ പരിഷ്ക്കരിക്കാനോ അപ്ലിക്കേഷനെ അനുവദിക്കാനിടയുണ്ട്. ശ്രദ്ധിക്കുക: ഈ അനുമതി മൂന്നാം കക്ഷി ബ്രൗസറുകളോ വെബ് ബ്രൗസിംഗ് കഴിവുകളുള്ള മറ്റ് അപ്ലിക്കേഷനുകളോ നടപ്പിലാക്കാനിടയില്ല."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"ഒരു അലാറം സജ്ജീകരിക്കുക"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"ഒരു ഇൻസ്റ്റാളുചെയ്ത അലാറം ക്ലോക്ക് അപ്ലിക്കേഷനിൽ അലാറം സജ്ജീകരിക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ചില അലാറം ക്ലോക്ക് അപ്ലിക്കേഷനുകൾ ഈ സവിശേഷത നടപ്പിലാക്കാതിരുന്നേക്കാം."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"വോയ്സ്മെയിലുകൾ റൈറ്റുചെയ്യുക"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"നിങ്ങളുടെ വോയ്സ്മെയിൽ ഇൻബോക്സിൽ നിന്നും സന്ദേശങ്ങൾ പരിഷ്ക്കരിക്കാനും നീക്കംചെയ്യാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"വോയ്സ് മെയിൽ ചേർക്കുക"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"നിങ്ങളുടെ വോയ്സ്മെയിൽ ഇൻബോക്സിലേക്ക് സന്ദേശങ്ങൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"വോയ്സ്മെയിൽ റീഡുചെയ്യുക"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"നിങ്ങളുടെ വോയ്സ്മെയിലുകൾ റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ബ്രൗസർ ജിയോലൊക്കേഷൻ അനുമതികൾ പരിഷ്ക്കരിക്കുക"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ബ്രൗസറിന്റെ ജിയോലൊക്കേഷൻ അനുമതികളെ പരിഷ്ക്കരിക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. അനിയന്ത്രിതമായ വെബ്സൈറ്റുകളിലേക്ക് ലൊക്കേഷൻ വിവരം അയയ്ക്കാൻ അനുവദിക്കുന്നതിന് ദോഷകരമായ അപ്ലിക്കേഷനുകൾ ഇത് ഉപയോഗിച്ചേക്കാം."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"പാക്കേജുകൾ പരിശോധിച്ചുറപ്പിക്കുക"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"സിസ്റ്റം"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth ഓഡിയോ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"വയർലെസ് ഡിസ്പ്ലേ"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"മീഡിയ ഔട്ട്പുട്ട്"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"ഉപകരണത്തിലേക്ക് കണക്റ്റുചെയ്യുക"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"സ്ക്രീൻ ഉപകരണത്തിലേക്ക് കാസ്റ്റുചെയ്യുക"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"ഉപകരണങ്ങൾക്കായി തിരയുന്നു…"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index ea33e8c..e5334a5 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -298,6 +298,8 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Апп нь Блютүүт MAP мессежийг хүлээн авах болон гүйцэтгэх боломжтой. Ингэснээр апп нь таны төхөөрөмжрүү илгээсэн мессежийг танд үзүүлэхгүйгээр хянах болон устгаж чадна."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ажиллаж байгаа апп-г дуудах"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Апп нь одоо ажиллаж байгаа болон сүүлд ажилласан даалгаврын талаарх мэдээллийг авах боломжтой. Ингэснээр апп нь төхөөмж дээрх ямар аппликешнүүд ашиглагдсан талаарх мэдээлийг олох боломжтой."</string>
+ <string name="permlab_startTasksFromRecents" msgid="8990073877885690623">"саяхных дотроос ажил эхлүүлэх"</string>
+ <string name="permdesc_startTasksFromRecents" msgid="7382133554871222235">"Апп-д ActivityManager.getRecentTaskList()-с буцаж ирсэн дууссан ажлыг эхлүүлэхийн тулд ActivityManager.RecentTaskInfo объектыг ашиглах боломж олгоно."</string>
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"хэрэглэгчидтэй харилцан үйлчлэлцэх"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Апп нь төхөөрөмж дээрх ялгаатай хэрэглэгчдэд үйлдэл гүйцэтгэх боломжтой. Хортой апп нь энийг ашиглан хэрэглэгч хоорондын хамгаалалтыг зөрчих боломжтой."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"хэрэглэгчидтэй харилцан үйлчлэлцэх бүрэн зөвшөөрөл"</string>
@@ -1555,7 +1557,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Систем"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Блютүүт аудио"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Утасгүй дэлгэц"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Медиа гаралт"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Төхөөрөмжтэй холбох"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Дэлгэцийг төхөөрөмж рүү дамжуулах"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Төхөөрөмжүүдийг хайж байна…"</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 73ff335..788d8b6 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Bluetooth नकाशा संदेश प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यास अॅप ला अनुमती देते. याचा अर्थ अॅप आपल्या डिव्हाइसवर पाठविलेले संदेश आपल्याला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"चालणारे अॅप्स पुनर्प्राप्त करा"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"सध्या आणि अलीकडे चालणार्या कार्यांविषयी माहिती पुनर्प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे डिव्हाइसवर कोणते अनुप्रयोग वापरले जात आहेत त्याविषयी माहिती शोधण्यासाठी अॅप ला अनुमती देऊ शकतात."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"वापरकर्त्यांशी परस्परसंवाद साधा"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"डिव्हाइसवरील भिन्न वापरकर्त्यांवर कारवाई करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स वापरकर्त्यांमधील संरक्षणाचे उल्लंघन करण्यासाठी हे वापरू शकतात."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"वापरकर्त्यांशी परस्परसंवाद साधण्यासाठी पूर्ण परवाना"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"आपल्या फोनवर संचयित केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अनुप्रयोगांद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"अलार्म सेट करा"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"स्थापित केलेल्या अलार्म घड्याळ अॅपमध्ये अलार्म सेट करण्यासाठी अॅप ला अनुमती देते. काही अलार्म घड्याळ अॅप्स हे वैशिष्ट्य लागू करू शकत नाहीत."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"व्हॉइसमेल लिहा"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"आपल्या व्हॉइसमेल इनबॉक्समधील संदेश सुधारित करण्यासाठी आणि ते काढण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"व्हॉइसमेल जोडा"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"आपल्या व्हॉइसमेल इनबॉक्समध्ये संदेश जोडण्यासाठी अॅप ला अनुमती देते."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"व्हॉइसमेल वाचा"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"आपले व्हॉइसमेल वाचण्याची अॅपला अनुमती देते."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ब्राउझर भौगोलिक स्थान परवानग्या सुधारित करा"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ब्राउझरच्या भौगोलिक स्थान परवानग्या सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स यादृच्छिक वेबसाइटवर स्थान माहिती पाठविण्यास अनुमती देण्यासाठी याचा वापर करू शकतात."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"पॅकेज सत्यापित करा"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"सिस्टम"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth ऑडिओ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"वायरलेस प्रदर्शन"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"माध्यम आउटपुट"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"डिव्हाइसला कनेक्ट करा"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"डिव्हाइसवर स्क्रीन कास्ट करा"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"डिव्हाइसेस शोधत आहे…"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 5fedab5..419c9b5 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Membenarkan apl menerima dan memproses mesej MAP Bluetooth. Ini bermakna apl boleh memantau atau memadam mesej yang dihantar ke peranti anda tanpa menunjukkannya kepada anda."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"dapatkan semula apl yang sedang dijalankan"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Membenarkan apl mengambil maklumat tentang tugasan yang sedang dan baru berjalan. Ini boleh membenarkan apl untuk menemui maklumat tentang apl mana yang digunakan pada peranti."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"berinteraksi sesama pengguna"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Membenarkan apl melakukan tindakan merentasi pengguna berbeza pada peranti. Apl hasad boleh menggunakan ini untuk melanggar perlindungan antara pengguna."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"lesen penuh untuk berinteraksi sesama pengguna"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Membenarkan apl mengubah suai sejarah atau penanda halaman Penyemak Imbas yang tersimpan pada telefon anda. Ini boleh membenarkan apl untuk memadam atau mengubah suai data Penyemak Imbas. Nota: kebenaran ini tidak boleh dikuatkuasakan oleh penyemak imbas pihak ketiga atau aplikasi lain dengan keupayaan menyemak imbas web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"tetapkan penggera"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Membenarkan apl untuk menetapkan penggera dalam apl penggera jam yang dipasang. Sesetengah applikasi jam penggera tidak boleh melaksanakan ciri ini."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"tulis mel suara"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Membenarkan apl mengubah suai dan mengalih keluar mesej dari peti masuk mel suara anda."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"tambah mel suara"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Membenarkan apl untuk menambahkan mesej pada peti masuk mel suara anda."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"baca mel suara"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Membenarkan apl membaca mel suara anda."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ubah suai kebenaran geolokasi Penyemak Imbas"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Membenarkan apl untuk mengubah suai kebenaran geolokasi Penyemak Imbas. Apl hasad boleh menggunakannya untuk membenarkan menghantar maklumat lokasi kepada laman web sembarangan."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"sahkan pakej"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Membenarkan aplikasi terikat kepada perkhidmatan ejen amanah."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Berinteraksi dengan kemas kini dan sistem pemulihan"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Membenarkan aplikasi berinteraksi dengan sistem pemulihan dan kemas kini sistem."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Buat sesi unjuran media"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Membenarkan aplikasi membuat sesi unjuran media. Sesi ini boleh memberikan aplikasi keupayaan untuk mengabadikan paparan dan kandungan audio. Tidak sekali-kali diperlukan oleh apl biasa."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Sentuh dua kali untuk mendapatkan kawalan zum"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Tidak dapat menambahkan widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Pergi"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Edit"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Amaran penggunaan data"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Sentuh untuk melihat penggunaan dan tetapan."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"Data 2G-3G dimatikan"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"Data 4G dimatikan"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Data selular dimatikan"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Data Wi-Fi dimatikan"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Sudah mencapai had"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Melebihi had data 2G-3G"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Melebihi had data 4G"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Melebihi had data seluluar"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Melebihi had data Wi-Fi"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> melebihi had yang ditentukan."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Data latar belakang terhad"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Paparan wayarles"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Output media"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Sambung ke peranti"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Hantar skrin ke peranti"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Mencari peranti..."</string>
@@ -1752,25 +1745,16 @@
<string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> dipilih"</string>
<string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> dipadamkan"</string>
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Kerja <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for lock_to_app_toast (2126866321272822564) -->
- <skip />
+ <string name="lock_to_app_toast" msgid="2126866321272822564">"Anda berada dalam mod kunci ke apl. Untuk keluar, sentuh dan tahan butang Terbaru"</string>
<string name="lock_to_app_toast_locked" msgid="4229650395479263497">"Anda berada dalam mod Kunci ke Apl."</string>
<string name="lock_to_app_title" msgid="5895142291937470019">"Gunakan kunci ke apl?"</string>
- <!-- no translation found for lock_to_app_description (2800403592608529611) -->
- <skip />
- <!-- no translation found for lock_to_app_negative (2259143719362732728) -->
- <skip />
+ <string name="lock_to_app_description" msgid="2800403592608529611">"Kunci dalam apl mengunci paparan dalam apl tunggal.\n\nUntuk keluar, sentuh dan tahan butang Terbaru."</string>
+ <string name="lock_to_app_negative" msgid="2259143719362732728">"TIDAK, TERIMA KASIH"</string>
<string name="lock_to_app_positive" msgid="7085139175671313864">"MULA"</string>
- <!-- no translation found for lock_to_app_start (3074665051586318340) -->
- <skip />
- <!-- no translation found for lock_to_app_exit (8967089657201849300) -->
- <skip />
- <!-- no translation found for lock_to_app_use_screen_lock (1434584309048590886) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_pin (7908385370846820001) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_pattern (7763071104790758405) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_password (795224196583495868) -->
- <skip />
+ <string name="lock_to_app_start" msgid="3074665051586318340">"Dikunci ke apl"</string>
+ <string name="lock_to_app_exit" msgid="8967089657201849300">"Tidak lagi dikunci ke apl"</string>
+ <string name="lock_to_app_use_screen_lock" msgid="1434584309048590886">"Minta %1$s sebelum keluar"</string>
+ <string name="lock_to_app_unlock_pin" msgid="7908385370846820001">"PIN"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="7763071104790758405">"corak buka kunci"</string>
+ <string name="lock_to_app_unlock_password" msgid="795224196583495868">"kata laluan"</string>
</resources>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index f3fc7cc..24be03d 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Bluetooth MAP စာများကို app မှလက်ခံကာ အလုပ်လုပ်ရန် ခွင့်ပြင်မည်။ ဆိုလိုသည်မှာ app သည်သင့်အား မပြသဘဲ သင့်ကိရိယာသို့ပို့လိုက်သည့် စာများကို ထိန်းချုပ်နိုင် သို့မဟုတ် ဖျက်နိုင်ပါသည်။"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"အလုပ်လုပ်နေကြသည့် appများကို ရယူခြင်း"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"အပလီကေးရှင်းအား လက်ရှိနဲ့ လတ်တလော လုပ်ဆောင်ခဲ့သော သတင်းအချက်အလက် အသေးစိတ်အား ထုတ်ယူခွင့်ပြုရန်။ အပလီကေးရှင်းမှ သင် ဘယ် အပလီကေးရှင်းများသုံးရှိကြောင့် တွေ့ရှိနိုင်ပါသည်"</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"အသုံးပြုသူများအကြား ဆက်ဆံခြင်း"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"အပလီကေးရှင်းအား စက်ပေါ်ရှိ တစ်ယောက်ထက်ပိုသော အသုံးပြုသူများအတွက် လုပ်ဆောင်ချက်များ ပြုလုပ်ခွင့်ပေးပါ။ အန္တရာယ်ရှိသော အပလီကေးရှင်းများမှ ဒီအရာကို သုံးပြီး အသုံးပြုသူများအတွင်း ကာကွယ်မှုကို ဖောက်ဖျက်နိုင်ပါသည်"</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"အသုံးပြုသူများအကြား ဆက်ဆံရန် လိုင်စင် အပြည့်"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"အပလီကေးရှင်းအား ဘရောင်ဇာမှ မှတ်တမ်း သို့ မှတ်သားမှု အမှတ်များအား ပြင်ဆင်ခွင့် ပေးခြင်း။ အပလီကေးရှင်းမှ ဘရောင်ဇာ မှတ်တမ်းများကို ဖျက်ပစ်ခွင့် သို့ ပြင်ဆင်ခွင့် ရှိပါမည်။ မှတ်ချက်။ ဒီခွင့်ပြုချက်ကို တတိယပါတီ ဘရောင်ဇာများ၊ တခြား အပလီကေးရှင်းများမှ သုံးမည် မဟုတ်ပါ။"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"နှိုးစက်သတ်မှတ်ရန်"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"appအား တပ်ဆင်ထားသည့် နှိုးစက်နာရီ app ထဲတွင် နှိုးစက်ကို သတ်မှတ်ခွင့် ပြုသည်။ အချို့ နှိုးစက် appများက ထိုအင်္ဂါရပ်ကို ပြီးမြောက်အောင် မလုပ်နိုင်ကြပါ။"</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"အသံမေးလ်ကို ရေးရန်"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"appအား သင်၏ အသံမေးလ် ဝင်စာများကို မွမ်းမံခွင့် နှင့် ဖယ်ရှားခွင့် ပြုသည်။"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"အသံစာပို့စနစ်အားထည့်ရန်"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"appအား သင့် အသံမေးလ် ဝင်စာသို့ စာများကို ထည့်ခွင့် ပြုသည်။"</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"အသံမေးလ်ကို ဖတ်ရန်"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"appအား သင်၏ အသံမေးလ်များကို ဖတ်ခွင့် ပြုရန်"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ဘရောင်ဇာ ဘူမိဇုန်သတ်မှတ်မှု ခွင့်ပြုချက်များကို မွမ်းမံခြင်း"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"appအား ဘရောင်ဇာ၏ ဘူမိဇုန်သတ်မှတ်ရေး ခွင့်ပြုချက်များကို မွမ်းမံခွင့် ပြုသည်။ ကြံဖန် appများက ၎င်းကို အသုံးချပြီး လိုရာ ဝက်ဘ်ဆိုက်များသို့ တည်နေရာ အချက်အလက် ပို့မှုကို လုပ်နိုင်သည်။"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"packages များကိုအတည်ပြုစိစစ်ခြင်း"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"စနစ်"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ဘလူးတုသ် အသံ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ကြိုးမဲ့ပြသခြင်း"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"မီဒီယာ အထွက်"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"စက်တစ်ခုကို ချိတ်ဆက်ရန်"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"ဖန်သားပြင်ကို စက်ဆီ ပို့လွှတ်ပါ"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"စက်များကို ရှာဖွေနေပါသည် ..."</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 502e0b8..f1079d1 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Appen gis tillatelse til å motta og behandle Bluetooth MAP-meldinger. Dette betyr at appen kan overvåke eller slette meldinger som er sendt til enheten din, uten at du får se dem."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"hente apper som kjører"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Lar appen hente informasjon om oppgaver som kjører og som nylig har kjørt. Dette kan tillate appen å oppdage informasjon om hvilke apper som brukes på enheten."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"samhandling på tvers av brukere"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Tillater at appen utfører handlinger på tvers av ulike brukere på enheten. Skadelige apper kan utnytte dette til å bryte beskyttelsen mellom brukere."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"full lisens til å samhandle på tvers av brukere"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Lar appen endre nettleserens logg eller bokmerker lagret på telefonen din. Dette kan føre til at appen sletter eller endrer nettleserdata. Vær oppmerksom på at denne tillatelsen kanskje ikke benyttes av tredjepartsnettlesere eller andre apper med mulighet for nettsurfing."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"stille alarm"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Lar appen stille inn alarmen for en installert alarmklokke-app. Enkelte alarmklokke-apper implementerer kanskje ikke denne funksjonen."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"skrive talepost"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Lar appen endre og fjerne meldinger fra talepostkassen."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"legge til talepost"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Lar appen legge til meldinger i talepostkassen din."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"lese talepost"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Lar appen lese taleposten din."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"endre nettleserens tillatelser for geoposisjonering"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Lar appen endre nettleserens tillatelser for geoposisjonering. Ondsinnede apper kan bruke dette for å tillate sending av posisjonsinformasjon til vilkårlige nettsteder."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"bekrefte pakker"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-lyd"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Trådløs skjerm"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Medieutgang"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Koble til enheten"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Cast skjermen til enheten"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Søker etter enheter …"</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index bff75b5..64d4c62 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"अनुप्रयोगलाई ब्लुटुथ MAP सन्देशहरू प्राप्त गर्न र प्रक्रिया गर्न अनुमति दिन्छ। यो अनुप्रयोगले तपाईँलाई नदेखाई आफ्नो उपकरणमा पठाइएको सन्देशहरू अनुगमन वा मेटाउन सक्दछ।"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"चलिरहेका अनुप्रयोगहरू पुनःबहाली गर्नुहोस्"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"वर्तमानमा र भरखरै चलिरहेका कार्यहरू बारेको सूचना पुनःबहाली गर्न अनुप्रयोगलाई अनुमित दिन्छ। यसले उपकरणमा प्रयोग भएका अनुप्रयोगहरूको बारेमा सूचना पत्ता लगाउन अनुप्रयोगलाई अनुमति दिन सक्छ।"</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"प्रयोगकर्ताहरू तर्फ अन्तर्क्रिया गर्नुहोस्"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"अनुप्रयोगलाई उपकरणमा विभिन्न प्रयोगकर्ताहरू मार्फत कार्यहरू गर्न अनुमति दिन्छ। खराब अनुप्रयोगहरूले यो प्रयोगकर्ताहरू बिच सुरक्षा बिथोल्न प्रयोग गर्न सक्ने छन्।"</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"कुराकानी प्रयोगकर्ताहरू बिच अन्तर्क्रिया गर्न पूर्ण अनुमति"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"तपाईँको फोनमा भण्डारण भएको ब्राउजरको इतिहास वा बुकमार्कहरू परिवर्तन गर्नको लागि अनुप्रयोगलाई अनुमति दिन्छ। यसले सायद ब्राउजर डेटालाई मेट्न वा परिवर्तन गर्नको लागि अनुप्रयोगलाई अनुमति दिन्छ। नोट: वेब ब्राउज गर्ने क्षमतासहितका अन्य अनुप्रयोगहरू वा तेस्रो- पक्ष ब्राउजरद्वारा सायद यस अनुमतिलाई लागु गर्न सकिंदैन।"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"एउटा आलर्म सेट गर्नुहोस्"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"स्थापना गरिएको सङ्केत घडी अनुप्रयोगमा सङ्केत समय मिलाउन अनुप्रयोगलाई अनुमति दिन्छ। केही सङ्केत घडी अनुप्रयोगहरूले यो सुविधा कार्यान्वयन नगर्न सक्छन्।"</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"भ्वाइसमेलहरू लेख्नुहोस्"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"तपाईँको भ्वाइसमेल इनबक्समा सन्देश परिमार्जन गर्न र हटाउन अनुप्रयोगलाई अनुमति दिनुहोस्।"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"भ्वाइसमेल थप गर्नुहोस्"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"तपाईँको भ्वाइसमेल इनबक्समा सन्देश थप्नको लागि अनुप्रयोगलाई अनुमति दिन्छ।"</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"भ्वाइसमेल पढ्नुहोस्"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"तपाईँको भ्वाइसमेलहरु पढ्न अनुमति दिन्छ।"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"भूस्थान अनुमतिहरू ब्राउजर परिवर्तन गर्नुहोस्"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ब्राउजरको भू-स्थान अनुमतिहरू परिमार्जन गर्न अनुप्रयोगलाई अनुमति दिन्छ। खराब अनुप्रयोगहरूले स्थान सूचना मनपरी वेब साइटहरूमा पठाउने अनुमतिको लागि यसलाई प्रयोग गर्न सक्छन्।"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"प्यकेजहरूको निरीक्षण गर्नुहोस्"</string>
@@ -1398,10 +1398,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"विश्वस्त प्रतिनिधि सेवालाई बाँध्न अनुप्रयोगलाई अनुमति दिन्छ।"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"अद्यावधिक र रिकभरी प्रणालीको साथ अन्तर्क्रिया"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"अनुप्रयोगलाई रिकभरी प्रणाली र प्रणाली अद्यावधिकहरूको साथ अन्तर्क्रिया गर्न अनुमति दिन्छ।"</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"मिडिया प्रक्षेपण सत्रहरू सिर्जना गर्नुहोस्"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"मिडिया प्रक्षेपण सत्र सिर्जना गर्न अनुप्रयोगलाई अनुमति दिन्छ। यी सत्रले प्रदर्शन र अडियो सामग्री खिच्ने क्षमताका अनुप्रयोगहरू प्रदान गर्न सक्छन्। सामान्य अनुप्रयोगहरूको कहिल्यै पनि आवश्यक पर्दैन।"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"जुम नियन्त्रणको लागि दुई चोटि टच गर्नुहोस्"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"विजेट थप गर्न सकिँदैन।"</string>
<string name="ime_action_go" msgid="8320845651737369027">"जानुहोस्"</string>
@@ -1526,20 +1524,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"सम्पादन गर्नुहोस्"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"डेटा प्रयोग चेतावनी"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"उपयोग र सेटिङहरू हेर्न छुनुहोस्।"</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G-3G डेटा बन्द छ"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G डेटा बन्द छ"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"सेलुलर डेटा बन्द छ"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"वाइफाइ डेटा बन्द छ"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"सीमा पुग्यो"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G-3G डेटा सीमा भन्दा पार भएको छ"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4G डेटा SIMा नाघ्यो"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"सेलुलर डेटा सीमा नाघ्यो"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"वाइफाइ डेटा SIMा नाघ्यो"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> उल्लेखित सीमा भन्दा बढी छ।"</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"पृष्ठभूमिका डेटा प्रतिबन्धित गरिएको छ"</string>
@@ -1575,7 +1567,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"प्रणाली"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ब्लुटुथ अडियो"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ताररहित प्रदर्शन"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"मिडियाको उत्पादन"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"उपकरणमा जडान गर्नुहोस्"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"उपकरणलाई स्क्रिनमा कास्ट गर्नुहोस्"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"उपकरणको खोजी गरिँदै..."</string>
@@ -1760,25 +1753,18 @@
<string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> चयन गरियो"</string>
<string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> हटाइयो"</string>
<string name="managed_profile_label_badge" msgid="2355652472854327647">"कार्य <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for lock_to_app_toast (2126866321272822564) -->
- <skip />
+ <string name="lock_to_app_toast" msgid="2126866321272822564">"तपाईँ अनुप्रयोग बन्द गर्ने ढाँचामा हुनुहुन्छ। निस्कनका लागि हालैको अनुप्रयोगहरूका बटन थिच्नुहोस् र समाउनुहोस्।"</string>
<string name="lock_to_app_toast_locked" msgid="4229650395479263497">"तपाईं Lock-to-App मोडमा हुनुहुन्छ।"</string>
<string name="lock_to_app_title" msgid="5895142291937470019">"लक-देखि-अनुप्रयोग प्रयोग गर्ने?"</string>
- <!-- no translation found for lock_to_app_description (2800403592608529611) -->
- <skip />
- <!-- no translation found for lock_to_app_negative (2259143719362732728) -->
- <skip />
+ <string name="lock_to_app_description" msgid="2800403592608529611">"लक-गर्न अनुप्रयोगले एकल अनुप्रयोगमा प्रदर्शन बन्द गर्छ।\n\n,निस्कनका लागि हालैको अनुप्रयोगहरूका बटन थिच्नुहोस् र समाउनुहोस्।"</string>
+ <string name="lock_to_app_negative" msgid="2259143719362732728">"होइन, धन्यवाद"</string>
<string name="lock_to_app_positive" msgid="7085139175671313864">"START"</string>
- <!-- no translation found for lock_to_app_start (3074665051586318340) -->
- <skip />
- <!-- no translation found for lock_to_app_exit (8967089657201849300) -->
- <skip />
+ <string name="lock_to_app_start" msgid="3074665051586318340">"अनुप्रयोग बन्द"</string>
+ <string name="lock_to_app_exit" msgid="8967089657201849300">"अनुप्रयोग अब बन्द छैन"</string>
+ <!-- String.format failed for translation -->
<!-- no translation found for lock_to_app_use_screen_lock (1434584309048590886) -->
<skip />
- <!-- no translation found for lock_to_app_unlock_pin (7908385370846820001) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_pattern (7763071104790758405) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_password (795224196583495868) -->
- <skip />
+ <string name="lock_to_app_unlock_pin" msgid="7908385370846820001">"PIN"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="7763071104790758405">"शैली बन्द गर्नुहोस"</string>
+ <string name="lock_to_app_unlock_password" msgid="795224196583495868">"पासवर्ड"</string>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 89c18a2..8270df9 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Hiermee kan de app Bluetooth MAP-berichten ontvangen en verwerken. Dit betekent dat de app berichten die naar uw apparaat zijn verzonden, kan controleren of verwijderen zonder ze aan u te laten zien."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"actieve apps ophalen"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Hiermee kan de app informatie ophalen over actieve en recent uitgevoerde taken. Zo kan de app informatie vinden over welke apps op het apparaat worden gebruikt."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interactie tussen gebruikers"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Hiermee kan de app acties uitvoeren voor verschillende gebruikers van het apparaat. Schadelijke apps kunnen dit gebruiken om de beveiliging tussen gebruikers te schenden."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"volledige toestemming voor interactie tussen gebruikers"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Hiermee kan de app de webgeschiedenis wijzigen in de systeemeigen browser en de bladwijzers die zijn opgeslagen op uw telefoon. Deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"een alarm instellen"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Hiermee kan de app een alarm instellen in een geïnstalleerde wekkerapp. Deze functie wordt door sommige wekkerapps niet geïmplementeerd."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"voicemails schrijven"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Hiermee kan de app berichten in de inbox van uw voicemail aanpassen en verwijderen."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"voicemail toevoegen"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Hiermee kan de app berichten toevoegen aan de inbox van uw voicemail."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"voicemail lezen"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Hiermee kan de app uw voicemails lezen."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"geolocatierechten voor browser aanpassen"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Hiermee kan de app de geolocatierechten van de browser aanpassen. Schadelijke apps kunnen dit gebruiken om locatiegegevens te verzenden naar willekeurige websites."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"pakketten controleren"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Systeem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Draadloze weergave"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Media-uitvoer"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Verbinding maken met apparaat"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Scherm casten naar apparaat"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Zoeken naar apparaten…"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 9da0d6d..4bc6541 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Pozwala aplikacji na odbieranie i przetwarzanie komunikatów Bluetooth MAP. Oznacza to, że może ona bez Twojej wiedzy monitorować i usuwać komunikaty przesyłane do Twojego urządzenia."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"pobieranie uruchomionych aplikacji"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Pozwala aplikacji na pobieranie informacji o aktualnie i niedawno działających zadaniach. Dzięki temu aplikacja może uzyskać informacje o tym, które aplikacje są używane na urządzeniu."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interakcje między użytkownikami"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Umożliwia aplikacji wykonywanie działań dotyczących różnych użytkowników urządzenia. Złośliwe aplikacje mogą to wykorzystać do złamania zabezpieczeń na kontach użytkowników."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"pełna licencja na interakcje między użytkownikami"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Pozwala aplikacji na modyfikowanie historii i zakładek przeglądarki zapisanych na telefonie. Aplikacja będzie mogła usunąć lub zmodyfikować dane przeglądarki. Uwaga: to uprawnienie może nie być egzekwowane przez przeglądarki innych firm oraz inne aplikacje z możliwością przeglądania internetu."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"ustawianie alarmu"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Pozwala aplikacji na ustawienie alarmu w zainstalowanej aplikacji budzika. Funkcja ta może nie być zaimplementowana w niektórych aplikacjach tego typu."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"zapisywanie poczty głosowej"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Zezwala aplikacji na modyfikowanie i usuwanie Twoich odebranych wiadomości poczty głosowej."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"dodawanie poczty głosowej"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Pozwala aplikacji na dodawanie wiadomości do skrzynki odbiorczej poczty głosowej."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"odczytywanie poczty głosowej"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Zezwala aplikacji na odczytywanie poczty głosowej."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modyfikowanie pozwoleń przeglądarki dotyczących lokalizacji geograficznej"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Pozwala aplikacji na modyfikowanie uprawnień przeglądarki dotyczących lokalizacji geograficznej. Złośliwe aplikacje mogą używać tej opcji do wysyłania informacji o lokalizacji do dowolnych witryn."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"weryfikowanie pakietów"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Dźwięk Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wyświetlacz bezprzewodowy"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Wyjście multimediów"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Połącz z urządzeniem"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Prezentuj ekran na urządzeniu"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Szukam urządzeń…"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 6769153..e782435 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Permite à aplicação receber e processar mensagens MAP Bluetooth, o que significa que a aplicação poderá monitorizar ou eliminar mensagens enviadas para o seu dispositivo sem lhas mostrar."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"obter aplicações em execução"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permite que a aplicação recupere informações acerca de tarefas executadas atual e recentemente. Isto pode permitir que a aplicação descubra informações acerca de quais as aplicações utilizadas no dispositivo."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interagir entre utilizadores"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Permite que a aplicação execute ações com diferentes utilizadores no dispositivo. Aplicações maliciosas poderão utilizar esta opção para violar a proteção entre utilizadores."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"licença completa para interagir entre utilizadores"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite que a aplicação modifique o histórico do Navegador ou marcadores guardados no telemóvel. Isto pode permitir que a aplicação apague ou modifique dados do Navegador. Nota: esta autorização pode não ser aplicada por navegadores de terceiros ou outras aplicações com capacidades de navegação na Web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"definir um alarme"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite que a aplicação defina um alarme numa aplicação de despertador instalada. Algumas aplicações de despertador podem não integrar esta funcionalidade."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"escrever mensagens de correio de voz"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Permite que a aplicação modifique e remova mensagens da caixa de entrada de correio de voz."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"adicionar correio de voz"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite que a aplicação adicione mensagens à sua caixa de entrada de correio de voz."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"ler correio de voz"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Permite que a aplicação leia as mensagens de correio de voz."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modificar permissões de geolocalização do Navegador"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite que a aplicação modifique as permissões de geolocalização do navegador. As aplicações maliciosas podem usar isto para permitir o envio de informações de localização para Web sites arbitrárias."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verificar pacotes"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Áudio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Visualização sem fios"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Saída de som multimédia"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Ligar ao dispositivo"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Transmitir ecrã para o dispositivo"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"A pesquisar dispositivos…"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index abc0b5e..114485b 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Permite que o aplicativo receba e processe mensagens Bluetooth MAP. Isso significa que o aplicativo pode monitorar ou excluir as mensagens enviadas para o dispositivo sem mostrá-las para você."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"recuperar aplicativos em execução"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permite que o aplicativo obtenha informações sobre tarefas em execução atuais e recentes. Pode permitir que o aplicativo descubra informações sobre os aplicativos usados no dispositivo."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interagir entre os usuários"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Permite que o aplicativo execute ações entre os diversos usuários do aparelho. Aplicativos mal-intencionados podem usar isto para violar a proteção entre os usuários."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"permissão total para interagir entre os usuários"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite que o aplicativo modifique o histórico ou os favoritos do navegador armazenados no telefone. Pode permitir que o aplicativo apague ou modifique os dados do navegador. Observação: pode não ser aplicável a navegadores de terceiros e outros aplicativos com capacidade de navegação na web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"definir um alarme"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite que o aplicativo defina um alarme em um aplicativo despertador instalado. Alguns aplicativos despertador podem não implementar este recurso."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"gravar correio de voz"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Permite que o app modifique e remova mensagens da caixa de entrada do correio de voz."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"adicionar correio de voz"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite que o aplicativo adicione mensagens a sua caixa de entrada do correio de voz."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"ler correio de voz"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Permite que o app leia seus correios de voz."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modifique as permissões de geolocalização de seu navegador"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite que o aplicativo modifique as permissões de geolocalização do navegador. Aplicativos maliciosos podem usar isso para permitir o envio de informações locais para sites arbitrários."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verificar pacotes"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite que o aplicativo se associe a um serviço de agente de confiança."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interagir com o sistema de atualizações e recuperação"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Permite que um aplicativo interaja com o sistema de recuperação e as atualizações do sistema."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Criar sessões de projeção de mídia"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Permite que um aplicativo crie sessões de projeção de mídia. As sessões podem fornecer aos aplicativos a capacidade de capturar conteúdo de tela e de áudio. Isso nunca deve ser necessário em aplicativos normais."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Toque duas vezes para controlar o zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Não foi possível adicionar widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Editar"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Aviso sobre uso de dados"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Toque p/ ver uso e config."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"Os dados 2G-3G foram desativados"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"Os dados 4G foram desativados"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Dados da rede cel. desativados"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Os dados Wi-Fi foram desativados"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Limite atingido"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Limite de dados 2G-3G excedido"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Limite de dados 4G excedido"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Lim. de dados rede cel. excedido"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Limite de dados Wi-Fi excedido"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> acima do limite especificado."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Dados de segundo plano restritos"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Áudio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Display sem fio"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Saída de mídia"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Conectar ao dispositivo"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Transmitir tela para dispositivo"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Procurando dispositivos…"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 024d827..84413bb 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Permite aplicației să primească și să proceseze mesaje MAP prin Bluetooth. Aceasta înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitiv fără a le afișa."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"preluare aplicaţii care rulează"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Permite aplicaţiei să preia informaţiile despre activităţile care rulează în prezent şi care au rulat recent. În acest fel, aplicaţia poate descoperi informaţii despre aplicaţiile care sunt utilizate pe dispozitiv."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interacţiune între utilizatori"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Permite aplicaţiei să efectueze acţiuni pentru diferiţi utilizatori pe dispozitiv. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a încălca protecţia între utilizatori."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"licenţă completă pentru interacţiune între utilizatori"</string>
@@ -719,8 +723,8 @@
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite proprietarului să se conecteze la interfața de nivel superior a unui serviciu de citire a notificărilor. În mod normal aplicațiile nu ar trebui să aibă nevoie de această permisiune."</string>
<string name="permlab_bindConditionProviderService" msgid="1180107672332704641">"conectare la un serviciu furnizor de condiții"</string>
<string name="permdesc_bindConditionProviderService" msgid="1680513931165058425">"Permite proprietarului să se conecteze la interfața de nivel superior a unui serviciu furnizor de condiții. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
- <string name="permlab_bindMediaRouteService" msgid="6637740382272686835">"conectare la un serviciu de trasee multimedia"</string>
- <string name="permdesc_bindMediaRouteService" msgid="6436655024972496687">"Permite proprietarului să se conecteze la interfața de nivel superior a unui serviciu de trasee multimedia. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
+ <string name="permlab_bindMediaRouteService" msgid="6637740382272686835">"se conectează la un serviciu de trasee multimedia"</string>
+ <string name="permdesc_bindMediaRouteService" msgid="6436655024972496687">"Permite deținătorului să se conecteze la interfața de nivel superior a unui serviciu de trasee multimedia. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
<string name="permlab_bindDreamService" msgid="4153646965978563462">"se conectează la un serviciu de vis"</string>
<string name="permdesc_bindDreamService" msgid="7325825272223347863">"Permite deținătorului să se conecteze la interfața superioară a unui serviciu de vis. Această opțiune nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"apelarea aplicației de configurare furnizată de operator"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite aplicaţiei să modifice istoricul Browserului sau marcajele stocate pe telefon. În acest fel, aplicaţia poate şterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicaţii cu capacităţi de navigare pe web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"setează o alarmă"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite aplicaţiei să seteze o alarmă într-o aplicaţie de ceas cu alarmă instalată. Este posibil ca unele aplicaţii de ceas cu alarmă să nu implementeze această funcţie."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"scrierea mesajelor vocale"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Permite aplicației să modifice și să elimine mesaje din secțiunea de mesaje vocale primite."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"adăugare mesagerie vocală"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite aplicaţiei să adauge mesaje în Mesaje primite în mesageria vocală."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"citirea mesajelor vocale"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Permite aplicației să citească mesajele vocale."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modificare permisiuni pentru locaţia geografică a browserului"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite aplicaţiei să modifice permisiunile privind locaţia geografică a browserului. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a permite trimiterea informaţiilor privind locaţia către site-uri web arbitrare."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verificare pachete"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite unei aplicații să se asocieze la un serviciu „agent de încredere”."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interacțiune cu sistemul de recuperare și de actualizare"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Permite unei aplicații să interacționeze cu sistemul de recuperare și cu actualizările de sistem."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Creați sesiuni de proiecție media"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Permite unei aplicații să creeze sesiuni de proiecție media. Aceste sesiuni pot permite aplicațiilor să captureze conținuturi vizuale și audio. Nu trebuie utilizată pentru aplicații obișnuite."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Atingeţi de două ori pentru a mări/micşora"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nu s-a putut adăuga widgetul."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Accesaţi"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Editaţi"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Avertisment de utiliz. a datelor"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Atingeţi pt. a afişa utiliz./set."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"Datele 2G-3G sunt dezactivate"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"Datele 4G sunt dezactivate"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Datele mobile sunt dezactivate"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Datele Wi-Fi sunt dezactivate"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Limita a fost atinsă"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"S-a depăşit limita de date 2G-3G"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"S-a depăşit limita de date 4G"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Limită de date mobile depășită"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"S-a depăşit limita de date Wi-Fi"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> peste limita specificată."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Datele de fundal restricţionate"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Ecran wireless"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Rezultate media"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Conectați-vă la dispozitiv"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Proiectați ecranul pe dispozitiv"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Se caută dispozitive..."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 218c3a54..99a7c13 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Получение и обработка сообщений Bluetooth MAP. Отслеживание и удаление непрочитанных сообщений."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"Получение данных о запущенных приложениях"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Приложение сможет получать информацию о недавно запущенных и выполняемых задачах, а следовательно, и о приложениях, используемых на устройстве."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"Взаимодействие с аккаунтами всех пользователей"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Приложение сможет выполнять действия во всех аккаунтах на этом устройстве. При этом защита от вредоносных приложений может быть недостаточной."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"Полное взаимодействие с аккаунтами всех пользователей"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Приложение сможет изменять историю или закладки браузера, сохраненные на телефоне, а также удалять и изменять данные браузера. Обратите внимание: браузеры независимых поставщиков или другие приложения для просмотра веб-страниц могут не применять это разрешение."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"Установка будильника"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Приложение сможет настраивать будильник. Функция поддерживается не во всех программах."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"запись голосовых сообщений"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Изменение и удаление сообщений из голосовой почты."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"Добавление голосовых сообщений"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Приложение сможет добавлять голосовые сообщения в папку \"Входящие\"."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"чтение голосовых сообщений"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Доступ к голосовой почте."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Изменение прав доступа к геоданным в браузере"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Приложение сможет изменять настройки доступа к геоданным в браузере. Вредоносные программы смогут таким образом отправлять информацию о местоположении на любые веб-сайты."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"Проверка пакетов"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Приложение сможет подключаться к службе Trust Agents."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Взаимодействовать с системой восстановления и обновлениями"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Приложение сможет взаимодействовать с системой восстановления и обновлениями системы."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Создание сеансов трансляции контента"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Создание сеансов трансляции контента, во время которых приложение получит доступ к изображению на экране и аудио. Это разрешение обычно используется только специальными приложениями."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Нажмите дважды для изменения масштаба"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Не удалось добавить виджет."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Выбрать"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Изменить"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Осталось мало трафика"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Проверьте трафик и настройки."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"Передача данных 2G/3G отключена"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"Передача данных 4G отключена"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Передача мобильных данных отключена"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Передача данных Wi-Fi отключена"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Достигнут лимит"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Превышен лимита трафика 2G и 3G"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Превышен лимит на трафик 4G"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Превышен лимит мобильного трафика"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Превышен лимит трафика Wi-Fi"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"Лимит превышен на <xliff:g id="SIZE">%s</xliff:g>."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Фоновый режим ограничен"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Система"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Воспроизведение звука через Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Беспроводной монитор"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Перенаправлять поток мультимедиа"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Подключение к устройству"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Подключение к удаленному дисплею"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Поиск устройств…"</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 331db28..b8622a5 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"බ්ලූටූත් MAP පණිවිඩ සොයා ලබාගැනීමට සහ ක්රියාත්මක කිරීමට යෙදුමට අවසර දෙන්න. යෙදුම නිරීක්ෂණය කරනු ලබන අතර ඔබට ලැබුන පණිවිඩ පෙන්වීමෙන් තොරවම මකා දැමිය හැකි බව මෙමඟින් අදහස් කරයි."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ධාවනය වන යෙදුම් ලබාගැනීම"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"දැනට සහ මෑත ක්රියාත්මක කාර්යයන් පිළිබඳ විස්තරාත්මක තොරතුරු සොයා ලබාගැනීමට යෙදුමට ඉඩ දෙන්න. මෙය කුමන යෙදුම් උපාංගයේ භාවිතා කරන්නේද යන තොරතුරු යෙදුම්වලට සොයා ගැනීමට ඉඩ දිය හැක."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"පරිශීලකයන් අතර අන්තර්ක්රියාකාරී වන්න"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"උපාංගයේ විවිධ පරිශීලකයන් හරහා ක්රියාවන් දැක්වීමට යෙදුමට අවසර දෙන්න. පරිශීලකයන් අතර ආරක්ෂාව කඩකිරීමට අනිෂ්ට යෙදුම් විසින් මෙය භාවිතා කිරීමට ඉඩ ඇත."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"පරිශීලකයන් අතර අන්තර් ක්රියාකාරී වීමට සම්පූර්ණ බලපත්රය"</string>
@@ -1008,16 +1012,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"ඔබගේ දුරකථනයේ ආචයනය කරන ලද බ්රව්සර ඉතිහාසය හෝ පිටුසන වෙනස් කිරීමට යෙදුමට අවසර දෙන්න. ඔබගේ බ්රව්සර දත්ත මැකීමට හෝ වෙනස් කිරීමට අනිෂ්ට යෙදුම් මෙය භාවිත කරයි. සටහන: වෙබ් බ්රව්සර අවශ්යතාවය සමග තෙවෙනි පාර්ශව බ්රව්සර හෝ වෙනත් යෙදුම් විසින් මෙම අවසරය බල ගැන්විය හැක."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"සීනුවක් සැකසීම"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"ස්ථාපනය කරන ලද සීනු ඔරලෝසු යෙදුමේ සීනුව සකස් කරන්නට යෙදුමට ඉඩ දෙන්න. ඇතැම් සීනු ඔරලෝසු යෙදුම් මෙම අංගය ක්රියාවට නංවා නොතිබීමට ඉඩ තිබේ."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"හඬ තැපෑල් ලියන්න"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"ඔබගේ හඬ තැපෑලේ එන ලිපි වෙත එන පණිවිඩ ඉවත් කිරීමට යෙදුමට අවසර දෙන්න."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"හඬ තැපෑල එක් කිරීම"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"ඔබගේ හඬ තැපෑලේ එන ලිපි වෙත එන පණිවිඩ එකතු කිරීමට යෙදුමට අවසර දෙන්න."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"හඬ තැපෑල් කියන්න"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"ඔබගේ හඬ තැපැල් කියවීමට යෙදුමට අවසර දේ."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"බ්රව්සරයේ භූ අවසර වෙනස් කිරීම"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"බ්රවුසරයේ භූ ස්ථානීය අවසර වෙනස් කිරීමට යෙදුමට අවසර දෙන්න. අභිමත වෙබ් අඩවි වලට ස්ථානීය තොරතුරු යැවීමට අනිෂ්ට යෙදුම් මෙය භාවිතා කෙරේ."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"පැකේජ සත්යාපනය කරන්න"</string>
@@ -1393,10 +1393,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"යෙදුමකට විශ්වාසනීය ඒජන්ත සේවාවකට බැඳීමට අවසර දේ."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"ප්රතිසාධන පද්ධතිය සහ යාවත්කාලීන සමඟ කටයුතු කරන්න"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"යෙදුමකට ප්රතිසාධන පද්ධතිය සහ පද්ධති යාවත්කාලීන සමඟ කටයුතු කිරීමට ඉඩ දෙන්න."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"මාධ්ය ප්රක්ෂේපන සැසියන් සාදන්න"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"මාධ්ය ප්රක්ෂේපන සැසියන් සාදන්න යෙදුමට ඉඩ දෙන්න. ශ්රව්ය අන්තර්ගතයන් සහ දර්ශන ලබා ගැනීමට හැකියාව යෙදුම් වලට මෙම සැසියන් ලබාගත හැක. සමාන්ය යෙදුම් සඳහා කවදාවත් අවශ්ය නොවේ."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"විශාලන පාලනය සඳහා දෙවරක් ස්පර්ශ කරන්න"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"විජටය එකතු කිරීමට නොහැකි විය."</string>
<string name="ime_action_go" msgid="8320845651737369027">"යන්න"</string>
@@ -1521,20 +1519,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"සංස්කරණය කරන්න"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"දත්ත භාවිතා අවවාදය"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"භාවිතය සහ සැකසීම් බැලීමට ස්පර්ශ කරන්න."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G-3G දත්ත නැත"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G දත්ත නැත"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"සෙලියුලර් දත්ත අක්රියයි"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi දත්ත අක්රියයි"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"සීමාව ළඟාවී ඇත"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G-3G දත්ත සීමාව ඉක්මවන ලදි"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4G දත්ත සීමාව ඉක්මවා යන ලදි"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"සෙලියුලර් දත්ත සීමාව ඉක්මවා තිබේ"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Wi-Fi දත්ත සීමාව ඉක්මවා යන ලදි"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"සඳහන් කළ සීමාවට වඩා <xliff:g id="SIZE">%s</xliff:g> වැඩිය."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"පසුබිම් දත්ත සිමා කරන ලදි"</string>
@@ -1570,7 +1562,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"පද්ධතිය"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"බ්ලූටූත් ශ්රව්ය"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"රැහැන් රහිත දර්ශනය"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"මාධ්ය ප්රතිදානය"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"උපාංගයට සම්බන්ධ වන්න"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"තිරය උපාංගයට යොමු කරන්න"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"උපාංග සඳහා සොයමින්…"</string>
@@ -1755,25 +1748,16 @@
<string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> තෝරාගෙන ඇත"</string>
<string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> මකා දමන ලදි"</string>
<string name="managed_profile_label_badge" msgid="2355652472854327647">"වැඩ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for lock_to_app_toast (2126866321272822564) -->
- <skip />
+ <string name="lock_to_app_toast" msgid="2126866321272822564">"යෙදුමට-අඟුළු දැමීමේ ආකාරය තුළ ඔබ සිටි. පිටවීමට, මෑත යෙදුම් බොත්තම ඔබා අල්ලා ගෙන සිටින්න"</string>
<string name="lock_to_app_toast_locked" msgid="4229650395479263497">"යෙදුමට-අඟුළු දැමීමේ ආකාරය තුළ ඔබ සිටි."</string>
<string name="lock_to_app_title" msgid="5895142291937470019">"අගුළු-යෙදුම භාවිතා කරන්නද?"</string>
- <!-- no translation found for lock_to_app_description (2800403592608529611) -->
- <skip />
- <!-- no translation found for lock_to_app_negative (2259143719362732728) -->
- <skip />
+ <string name="lock_to_app_description" msgid="2800403592608529611">"අගුළු-යෙදුම තනි යෙදුමක පෙනුම අගුළු දමයි.\n\nඉවත්ව යාමට මෑත යෙදුම් බොත්තම ඔබා රඳවා ගන්න"</string>
+ <string name="lock_to_app_negative" msgid="2259143719362732728">"නැත, ස්තූතියි"</string>
<string name="lock_to_app_positive" msgid="7085139175671313864">"ආරම්භය"</string>
- <!-- no translation found for lock_to_app_start (3074665051586318340) -->
- <skip />
- <!-- no translation found for lock_to_app_exit (8967089657201849300) -->
- <skip />
- <!-- no translation found for lock_to_app_use_screen_lock (1434584309048590886) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_pin (7908385370846820001) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_pattern (7763071104790758405) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_password (795224196583495868) -->
- <skip />
+ <string name="lock_to_app_start" msgid="3074665051586318340">"යෙදුමට-අඟුළු දැමීම"</string>
+ <string name="lock_to_app_exit" msgid="8967089657201849300">"යෙදුමට අඟුළු දැමීම තවදුරටත් නැත"</string>
+ <string name="lock_to_app_use_screen_lock" msgid="1434584309048590886">"පිටවීමට පෙර %1$s සඳහා ආසන්න"</string>
+ <string name="lock_to_app_unlock_pin" msgid="7908385370846820001">"PIN"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="7763071104790758405">"අඟුළු ඇරීමේ රටාව"</string>
+ <string name="lock_to_app_unlock_password" msgid="795224196583495868">"මුරපදය"</string>
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 00f22bb..fdfcd82 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Umožňuje aplikácii prijímať a spracovávať správy MAP rozhrania Bluetooth. Znamená to, že aplikácia môže sledovať správy odoslané na vaše zariadenie alebo ich odstrániť bez toho, aby sa vám zobrazili."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"načítať spustené aplikácie"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Umožňuje aplikácii načítať informácie o aktuálne či nedávno spustených úlohách. Toto povolenie môže aplikácii umožniť objaviť informácie o tom, ktoré aplikácie sa na zariadení používajú."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interakcie naprieč používateľmi"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Umožňuje aplikácii vykonávať akcie naprieč rôznymi používateľmi zariadenia. Škodlivé aplikácie môžu toto povolenie zneužiť na obídenie ochrany medzi používateľmi."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"plná licencia na interakcie naprieč používateľmi"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Umožňuje aplikácii upraviť históriu prehliadača alebo záložky uložené v telefóne. Aplikácia s týmto povolením môže vymazať alebo upraviť údaje prehliadača. Poznámka: Toto povolenie nemôžu vynucovať prehliadače tretích strán ani žiadne ďalšie aplikácie umožňujúce prehliadanie webu."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"nastaviť budík"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Umožňuje aplikácii nastaviť budík v nainštalovanej aplikácii budík. Niektoré aplikácie budíka nemusia túto funkciu implementovať."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"zapisovanie do hlasovej schránky"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Povoľuje aplikácii upravovať a odstraňovať správy z hlasovej schránky."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"pridať hlasovú schránku"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Umožní aplikácii pridávať správy do doručenej pošty hlasovej schránky."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"čítanie hlasových správ"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Umožňuje aplikácii čítať vaše hlasové správy."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"zmeniť povolenia prehliadača poskytovať informácie o zemepisnej polohe"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Umožňuje aplikácii zmeniť povolenia prehliadača na poskytovanie údajov o zemepisnej polohe. Škodlivé aplikácie to môžu použiť na odosielanie informácií o polohe ľubovoľným webovým stránkam."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"overiť balíky"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Umožňuje aplikácii viazať sa na službu zástupcu dôveryhodnosti."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interakcia so systémom aktualizácií a obnovenia"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Umožňuje aplikácii interakciu so systémom obnovenia a s aktualizáciami systému."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Vytváranie relácií projekcie médií"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Umožňuje aplikácii vytvárať relácie projekcie médií. Tieto relácie môžu aplikáciám umožniť zaznamenávať obsah na obrazovke a zvukový obsah. Bežné aplikácie by toto povolenie nemali nikdy potrebovať."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Ovládacie prvky lupy zobrazíte dvojitým dotknutím"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Miniaplikáciu sa nepodarilo pridať."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Hľadať"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Upraviť"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Upozornenie o využití dát"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Zobr. využív. dát a nastavení."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"dáta 2G–3G sú vypnuté"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"dáta 4G sú vypnuté"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Mobilné dáta sú vypnuté"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Dáta siete Wi-Fi sú vypnuté"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Dosiahli ste limit"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G, 3G dátový limit prekročený"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Dátový limit 4G bol prekročený"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Presiahli ste limit mobil. dát"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Dát. limit Wi-Fi bol prekročený"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> nad stanovenou hranicou."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Údaje na pozadí sú obmedzené"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Systém"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bezdrôtový displej"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Výstup médií"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Pripojenie k zariadeniu"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Prenos obraz. do zariad."</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Prebieha vyhľadávanie zariadení…"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index bfc28ca..2cd858e 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Aplikaciji omogoča prejemanje in obdelavo sporočil Bluetooth MAP. To pomeni, da lahko aplikacija nadzira in izbriše sporočila, poslana v napravo, ne da bi vam jih prikazala."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"dobivanje programov, ki se izvajajo"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Aplikaciji omogoča prejemanje podatkov o trenutnih in nedavno izvajajočih se opravilih. S tem lahko aplikacija odkrije podatke o aplikacijah, ki se uporabljajo v napravi."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interakcija z uporabniki"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Aplikaciji omogoča izvajanje dejanj za različne uporabnike v napravi. Zlonamerne aplikacije lahko to uporabijo za kršitev zaščite med uporabniki."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"polna licenca za interakcijo z uporabniki"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Aplikaciji omogoča spreminjanje zgodovine ali zaznamkov brskalnika v telefonu. S tem lahko aplikacija izbriše ali spremeni podatke v brskalniku. Opomba: Tega dovoljenja ne morejo uveljavljati drugi brskalniki ali aplikacije, s katerimi je mogoče brskati po spletu."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"nastavitev alarma"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Programu omogoča nastavitev alarma v nameščenem programu budilke. Nekateri programi budilke morda nimajo te funkcije."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"snemanje sporočil v odzivniku"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Aplikaciji omogoča spreminjanje in odstranjevanje sporočil iz odzivnika."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"dodajanje odzivnika"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Programu omogoča dodajanje sporočil prejetim sporočilom odzivnika."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"branje sporočil v odzivniku"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Aplikaciji omogoča branje sporočil v odzivniku."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Spreminjanje dovoljenj za geolokacijo brskalnika"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Programu omogoča spreminjanje geolokacijskih dovoljenj v brskalniku. Zlonamerni programi lahko to izkoristijo za pošiljanje podatkov o lokaciji poljubnim spletnim mestom."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"preveri pakete"</string>
@@ -1186,9 +1186,9 @@
<string name="whichApplicationNamed" msgid="8260158865936942783">"Dokončanje dejanja z aplikacijo %1$s"</string>
<string name="whichViewApplication" msgid="3272778576700572102">"Odpiranje z aplikacijo"</string>
<string name="whichViewApplicationNamed" msgid="2286418824011249620">"Odpiranje z aplikacijo %1$s"</string>
- <string name="whichEditApplication" msgid="144727838241402655">"Urejanje z"</string>
+ <string name="whichEditApplication" msgid="144727838241402655">"Urejanje z aplikacijo"</string>
<string name="whichEditApplicationNamed" msgid="1775815530156447790">"Urejanje z aplikacijo %1$s"</string>
- <string name="whichSendApplication" msgid="6902512414057341668">"Skupna raba z"</string>
+ <string name="whichSendApplication" msgid="6902512414057341668">"Skupna raba z aplikacijo"</string>
<string name="whichSendApplicationNamed" msgid="2799370240005424391">"Skupna raba z aplikacijo %1$s"</string>
<string name="whichHomeApplication" msgid="4616420172727326782">"Izberite aplikacijo za začetno stran"</string>
<string name="alwaysUse" msgid="4583018368000610438">"Privzeta uporaba za to dejanje."</string>
@@ -1516,10 +1516,10 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Uredi"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Opozorilo o uporabi podatkov"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Dotaknite se za uporabo in nast."</string>
- <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"Prenos pod. v omr. 2G/3G je izk."</string>
- <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"Prenos pod. v omrež. 4G je izkl."</string>
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"Podatki v omrežju 2G/3G so izkl."</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"Podatki v omrežju 4G so izklop."</string>
<string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Prenos mob. podatkov izklopljen"</string>
- <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Prenos pod. v om. Wi-Fi je izkl."</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Prenos pod. prek Wi-Fi je izkl."</string>
<string name="data_usage_limit_body" msgid="6131350187562939365">"Omejitev je dosežena"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Omejit. za podat. 2G-3G presež."</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Omejitev za podat. 4G presež."</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Zvok prek Bluetootha"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Brezžični prikaz"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Izhod predstavnosti"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Povezovanje z napravo"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Predvajanje zaslona v napravi"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Iskanje naprav …"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 25e3c8d..25bf1a9 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Дозвољава апликацији да прима и обрађује Bluetooth MAP поруке. То значи да апликација може да надгледа или брише поруке које се шаљу на уређај, а да вам их не прикаже."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"преузимање покренутих апликација"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Дозвољава апликацији да преузима информације о актуелним и недавно покренутим задацима. Ово може да омогући апликацији да открије информације о томе које се апликације користе на уређају."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"интеракција између корисника"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Дозвољава апликацији да обавља радње између различитих корисника на уређају. Злонамерне апликације могу да користе ово да би угрозиле заштиту између корисника."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"пуна лиценца за интеракцију између корисника"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Дозвољава апликацији да мења историју Прегледача или обележиваче ускладиштене на телефону. Ово може да омогући апликацији да брише или мења податке Прегледача. Напомена: Ова дозвола се можда на примењује на прегледаче треће стране и друге апликације са могућношћу веб прегледања."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"подешавање аларма"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Дозвољава апликацији да подеси аларм у инсталираној апликацији будилника. Неке апликације будилника можда не примењују ову функцију."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"пиши поруке говорне поште"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Дозвољава апликацији да мења и уклања поруке из пријемног сандучета говорне поште."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"додавање говорне поште"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Дозвољава апликацији да додаје поруке у пријемно сандуче говорне поште."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"читај говорну пошту"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Дозвољава апликацији да чита поруке говорне поште."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"измена дозвола за географске локације Прегледача"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Дозвољава апликацији да измени дозволе Прегледача за утврђивање географске локације. Злонамерне апликације то могу да злоупотребе и искористе за слање информација о локацији насумичним веб сајтовима."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"верификовање пакета"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Дозвољава апликацији да се веже за услугу Trust agents."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Интеракција са системом за ажурирање и опоравак"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Дозвољава апликацији да ступа у интеракцију са системом за опоравак и ажурирањима система."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Прави сесије пројекција медија"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Омогућава апликацији да прави сесије пројекција медија. Ове сесије могу да омогуће апликацијама да снимају садржај на екрану или аудио садржај. Никада не би требало да буде потребно за стандардне апликације."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Додирните двапут да бисте контролисали зум"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Није могуће додати виџет."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Иди"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Измени"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Упозорење о потрошњи података"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Додирните за преглед кор. и под."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G–3G подаци су искључени"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G подаци су искључени"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Мобилни подаци су искључени"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi подаци су искључени"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Ограничење је достигнуто"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Прекорачен пренос 2G-3G података"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Прекорачење преноса 4G података"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Ограничење мобилних података је прекорачено"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Прекорачење преноса Wi-Fi подат."</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> преко наведеног ограничења."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Позадински подаци су ограничени"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Систем"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth аудио"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Бежични екран"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Излаз медија"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Повежите са уређајем"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Пребаците екран на уређај"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Тражење уређаја…"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 6b885d9..7eee11b 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Tillåter att appen tar emot och hanterar Bluetooth MAP-meddelanden. Detta innebär att appen kan övervaka eller ta bort meddelanden som skickats till enheten utan att visa dem för dig."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"hämta appar som körs"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Tillåter att appen hämtar information om nyligen körda och pågående aktiviteter. Detta kan innebära att appen tillåts ta reda på vilka appar som används på enheten."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interagera mellan användare"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Tillåter att appen utför åtgärder mellan användare på enheten. Skadliga appar kan använda detta som ett sätt att kringgå skyddet mellan användare."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"fullständig behörighet att interagera mellan användare"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på telefonen. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"ställa in ett alarm"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Tillåter att appen ställer in ett alarm i en befintlig alarmapp. Vissa alarmappar har inte den här funktionen."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"skriva röstmeddelanden"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Tillåter att appen ändrar och tar bort meddelanden från röstbrevlådans inkorg."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"lägg till röstbrevlåda"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Gör att appen lägger till meddelanden i röstbrevlådans inkorg."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"läsa röstmeddelanden"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Tillåter att appen läser dina röstmeddelanden."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Ändra geografisk plats för webbläsaren"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Tillåter att appen ändrar webbläsarens behörigheter för geografisk plats. Skadliga appar kan använda detta för att tillåta att platsinformation skickas till godtyckliga webbplatser."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"kontrollera paket"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-ljud"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Trådlös skärm"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Medieuppspelning"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Anslut till enhet"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Överför skärmen till enheten"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Söker efter enheter ..."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index a9c3c52..a00ea16 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Inaruhusu programu kupokea na kuchakata ujumbe wa Bluetooth MAP. Hii inamaanisha programu inaweza kufuatilia au kufuta ujumbe unaotumwa kwenye kifaa chako pasipo kukuonyesha ujumbe huo."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"rudisha programu zinazoendeshwa"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Inaruhusu programu kurudisha taarifa kuhusu kazi zinazoendeshwa sasa na hivi karibuni. Hii inaweza kuruhusu programu kugundua taarifa kuhusu ni programu zipi zinazotumika kwenye kifaa."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"kuwasiliana na watumiaji wengine"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Inaruhusu programu kutenda vitendo kwa watumiaji tofauti kwenye kifaa. Programu hasidi huenda zikatumia hii ili kukiuka ulinzi kati ya watumiaji."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"leseni kamili ili kushirikiana na watumiaji"</string>
@@ -719,8 +723,8 @@
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Inaruhusu kishikilizi kuunganishwa kwenye kusano cha kiwango cha juu cha huduma ya kisikilizi cha arifa. Haipaswi kuhitajika tena kwa programu za kawaida."</string>
<string name="permlab_bindConditionProviderService" msgid="1180107672332704641">"bandika kwenye huduma ya mtoa masharti"</string>
<string name="permdesc_bindConditionProviderService" msgid="1680513931165058425">"Humruhusu mmiliki kubandika kwenye kiolesura cha kiwango cha juu cha huduma ya mtoa masharti. Isihitajike kamwe kwa pogramu za kawaida."</string>
- <string name="permlab_bindMediaRouteService" msgid="6637740382272686835">"shurutisha kwa huduma ya njia za sauti, picha na video."</string>
- <string name="permdesc_bindMediaRouteService" msgid="6436655024972496687">"Humruhusu mmiliki kushurutisha kwa kiolesura cha ngazi ya juu cha huduma ya njia za sauti, picha na video. Haipaswi kuhitajika kwa programu za kawaida."</string>
+ <string name="permlab_bindMediaRouteService" msgid="6637740382272686835">"bandika kwenye huduma ya njia za sauti, picha na video."</string>
+ <string name="permdesc_bindMediaRouteService" msgid="6436655024972496687">"Humruhusu mmiliki kubandika kwenye kiolesura cha ngazi ya juu cha huduma ya njia za sauti, picha na video. Haipaswi kuhitajika kwa programu za kawaida."</string>
<string name="permlab_bindDreamService" msgid="4153646965978563462">"shurutisha kwa huduma murua"</string>
<string name="permdesc_bindDreamService" msgid="7325825272223347863">"Huruhusu mmiliki kushurutisha kwenye kiolesura cha kiwango cha juu cha huduma murua. Haipaswi kuhitajika kwa programu za kawaida."</string>
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"omba programu ya usakinishaji inayotolewa na mtoa huduma."</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Inaruhusu programu kurekebisha historia ya Kivinjari au alamisho zilizohifadhiwa kwenye simu yako. Hii huenda ikaruhusu programu kufuta au kurekebisha data ya Kivinjari. Kumbuka: huenda idhini hii isitekelezwe na vivinjari vingine au programu nyingine zenye uwezo wa kuvinjari wavuti."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"weka kengele"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Inaruhusu programu kuweka kengele katika programu iliyosakinishwa ya kengele. Programu zingine za kengele zinawezakosa kutekeleza kipengee hiki."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"andika ujumbe wa sauti"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Huruhusu programu kubadili na kuondoa ujumbe kwenye kikasha chako cha ujumbe wa sauti."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ongeza barua ya sauti"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Huruhusu programu kuongeza mawasiliano kwenye kikasha cha ujumbe wa sauti."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"soma ujumbe wa sauti"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Huruhusu programu isome ujumbe wako wa sauti."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Rekebisha vibali vya Kivinjari cha eneo la jio"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Inaruhusu programu kurekebisha ruhusa za eneo la jio za kivinjari. Programu hasidi zinaweza tumia hii kuruhusu kutuma taarifa ya eneo kwa wavuti holela."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"thibitisha furushi"</string>
@@ -1292,10 +1292,10 @@
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"Ruhusu Kila mara"</string>
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"Usiruhusu Kamwe"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"SIM kadi imeondolewa"</string>
- <string name="sim_removed_message" msgid="5450336489923274918">"Mtandao wa simu za mkononi hautapatikana hadi utakapoanzisha tena kifaa chako kikiwa kimeingizwa SIM kadi sahihi."</string>
+ <string name="sim_removed_message" msgid="5450336489923274918">"Mitandao ya simu za mkononi haitapatikana hadi utakapozima na kuwasha tena kifaa chako kikiwa kimeingizwa SIM kadi sahihi."</string>
<string name="sim_done_button" msgid="827949989369963775">"Nimemaliza"</string>
<string name="sim_added_title" msgid="3719670512889674693">"SIM kadi imeongezwa"</string>
- <string name="sim_added_message" msgid="7797975656153714319">"Anzisha kifaa chako tena ili ufikie mitandao ya simu za mkononi."</string>
+ <string name="sim_added_message" msgid="7797975656153714319">"Zima na uwashe kifaa chako tena ili ufikie mitandao ya simu za mkononi."</string>
<string name="sim_restart_button" msgid="4722407842815232347">"Anza upya"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"Weka muda"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"Weka tarehe"</string>
@@ -1523,7 +1523,7 @@
<string name="data_usage_limit_body" msgid="6131350187562939365">"Kikomo kimefikiwa"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Kikomo cha data ya 2G-3G kimezidishwa"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Kikomo cha data cha 4G kimezidishwa"</string>
- <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Kikomo cha data ya simu za mkononi kimevukwa"</string>
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Umezidi kikomo cha data ya simu"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Taarifa za Wi-fi zimevuka kiwanga"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> juu ya kikomo kilichobainishwa."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Data ya mandhari nyuma imezuiwa"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Mfumo"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Sauti ya Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Uonyeshaji usiotumia waya"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Towe la midia"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Unganisha kwenye kifaa"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Tuma skrini kwenye kifaa"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Inatafuta vifaa..."</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index e74feb4..7d4478e 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"புளூடூத் MAP செய்திகளைப் பெற மற்றும் செயல்படுத்த பயன்பாட்டை அனுமதிக்கிறது. அதாவது பயன்பாட்டால் சாதனத்திற்கு அனுப்பப்பட்ட செய்திகளை, உங்களிடம் காட்டாமலே கண்காணிக்கவோ, நீக்கவோ முடியும்."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"இயங்கும் பயன்பாடுகளை மீட்டெடுத்தல்"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"நடப்பில் மற்றும் சமீபத்தில் இயங்கும் காரியங்களின் தகவலைப் பெற பயன்பாட்டை அனுமதிக்கிறது. சாதனத்தில் எந்தப் பயன்பாடுகள் பயன்படுத்தப்படுகின்றன என்பது குறித்த தகவலைக் கண்டறிய பயன்பாட்டை இது அனுமதிக்கலாம்."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"பிற பயனர்களுடன் தொடர்புகொள்ளுதல்"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"சாதனத்தில் உள்ள பல்வேறு பயனர்கள் அனைவரிலும் செயல்களைச் செய்ய பயன்பாட்டை அனுமதிக்கிறது. பயனர்கள் இடையேயான பாதுகாப்பை மீற தீங்கிழைக்கும் பயன்பாடுகள் இதைப் பயன்படுத்தலாம்."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"பிற பயனர்களுடன் தொடர்புகொள்வதற்கான முழு உரிமம்"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"உங்கள் மொபைலில் சேமிக்கப்பட்ட உலாவியின் வரலாறு அல்லது புத்தகக்குறிகளைத் திருத்த பயன்பாட்டை அனுமதிக்கிறது. இது உலாவியின் தரவை அழிக்கவோ, திருத்தவோ பயன்பாட்டை அனுமதிக்கலாம். குறிப்பு: இணைய உலாவல் செயல்திறன்கள் மூலம் மூன்றாம் தரப்பு உலாவிகள் அல்லது பிற பயன்பாடுகள் இந்த அனுமதியைச் செயற்படுத்த முடியாது."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"அலாரத்தை அமைத்தல்"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"நிறுவிய அலார கடிகாரப் பயன்பாட்டில் அலாரத்தை அமைக்க, பயன்பாட்டை அனுமதிக்கிறது. சில அலார கடிகார பயன்பாடுகளில் இந்த அம்சம் இல்லாமல் இருக்கலாம்."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"குரலஞ்சல்களை எழுது"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"குரலஞ்சல் இன்பாக்ஸிலிருந்து செய்திகளைத் திருத்தவும் அகற்றவும், பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"குரலஞ்சலைச் சேர்த்தல்"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"குரலஞ்சல் இன்பாக்ஸில் செய்திகளைச் சேர்க்க, பயன்பாட்டை அனுமதிக்கிறது."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"குரலஞ்சலைப் படி"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"குரலஞ்சல்களைப் படிக்கப் பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"உலாவியின் புவியியல் இருப்பிடம் சார்ந்த அனுமதிகளைத் திருத்துதல்"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"உலாவியின் புவியியல் இருப்பிடம் சார்ந்த அனுமதிகளைத் திருத்த, பயன்பாட்டை அனுமதிக்கிறது. இடத் தகவலை தன்னிச்சையான இணையதளங்களுக்கு அனுப்புவதை அனுமதிக்க, தீங்குவிளைவிக்கும் பயன்பாடுகள் இதைப் பயன்படுத்தலாம்."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"தொகுப்புகளைச் சரிபார்த்தல்"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"அமைப்பு"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"புளூடூத் ஆடியோ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"வயர்லெஸ் காட்சி"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"மீடியா வெளியீடு"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"சாதனத்துடன் இணைக்கவும்"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"திரையிலிருந்து சாதனத்திற்கு அனுப்புக"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"சாதனங்களைத் தேடுகிறது..."</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index f951d5c..1868818 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"బ్లూటూత్ MAP సందేశాలను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది. అనువర్తనం మీ పరికరానికి పంపబడిన సందేశాలను పర్యవేక్షించగలదని లేదా మీకు చూపకుండానే తొలగించగలదని దీనర్థం."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"అమలవుతున్న అనువర్తనాలను పునరుద్ధరించడం"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"ప్రస్తుతం మరియు ఇటీవల అమలవుతున్న విధుల గురించి వివరణాత్మక సమాచారాన్ని తిరిగి పొందడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది పరికరంలో ఉపయోగించబడిన అనువర్తనాల గురించి సమాచారాన్ని కనుగొనడానికి అనువర్తనాన్ని అనుమతించవచ్చు."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"వినియోగదారుల మధ్య పరస్పర చర్య చేయడం"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"పరికరంలోని విభిన్న వినియోగదారుల తరపున చర్యలను అమలు చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది. హానికరమైన అనువర్తనాలు వినియోగదారుల మధ్య రక్షణను ఉల్లంఘించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"వినియోగదారుల మధ్య పరస్పర చర్య చేయడానికి పూర్తి లైసెన్స్"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"మీ ఫోన్లో నిల్వ చేయబడిన బ్రౌజర్ చరిత్రను లేదా బుక్మార్క్లను సవరించడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను ఎరేజ్ చేయడానికి లేదా సవరించడానికి అనువర్తనాన్ని అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ పక్షం బ్రౌజర్లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర అనువర్తనాల ద్వారా అమలు చేయబడకపోవచ్చు."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"అలారం సెట్ చేయడం"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"ఇన్స్టాల్ చేయబడిన అలారం గడియారం అనువర్తనంలో అలారంను సెట్ చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది. కొన్ని అలారం గల గడియారం అనువర్తనాలు ఈ లక్షణాన్ని అమలు చేయకపోవచ్చు."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"వాయిస్ మెయిల్లను వ్రాయడం"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"మీ వాయిస్ మెయిల్ ఇన్బాక్స్ నుండి సందేశాలను సవరించడానికి మరియు తీసివేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"వాయిస్ మెయిల్ను జోడించడం"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"మీ వాయిస్ మెయిల్ ఇన్బాక్స్కి సందేశాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"వాయిస్ మెయిల్లను చదవడం"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"మీ వాయిస్ మెయిల్లను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"బ్రౌజర్ భౌగోళిక స్థానం అనుమతులను సవరించడం"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"బ్రౌజర్ యొక్క భౌగోళిక స్థానం అనుమతులను సవరించడానికి అనువర్తనాన్ని అనుమతిస్తుంది. హానికరమైన అనువర్తనాలు ఏకపక్ష వెబ్ సైట్లకు స్థాన సమాచారాన్ని అనుమతించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ప్యాకేజీలను ధృవీకరించడం"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"సిస్టమ్"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"బ్లూటూత్ ఆడియో"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"వైర్లెస్ డిస్ప్లే"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"మీడియా అవుట్పుట్"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"పరికరానికి కనెక్ట్ చేయండి"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"స్క్రీన్ను పరికరానికి ప్రసారం చేయండి"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"పరికరాల కోసం శోధిస్తోంది…"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index d546738..a5d7a36 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -162,7 +162,7 @@
<string name="silent_mode_silent" msgid="319298163018473078">"ปิดเสียง"</string>
<string name="silent_mode_vibrate" msgid="7072043388581551395">"เสียงเรียกเข้าแบบสั่น"</string>
<string name="silent_mode_ring" msgid="8592241816194074353">"เปิดเสียง"</string>
- <string name="shutdown_progress" msgid="2281079257329981203">"กำลังปิดระบบ..."</string>
+ <string name="shutdown_progress" msgid="2281079257329981203">"กำลังปิด..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"แท็บเล็ตของคุณจะปิดการทำงาน"</string>
<string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"นาฬิกาจะปิดการทำงาน"</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"โทรศัพท์ของคุณจะปิดเครื่อง"</string>
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"อนุญาตให้แอปรับและประมวลผลข้อความ MAP สำหรับบลูทูธ ซึ่งหมายความว่าแอปจะสามารถตรวจสอบหรือลบข้อความที่ส่งไปยังอุปกรณ์ของคุณได้โดยไม่ต้องแสดงให้คุณเห็น"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"เรียกแอปพลิเคชันที่ทำงานอยู่"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"อนุญาตให้แอปพลิเคชันเรียกดูข้อมูลเกี่ยวกับงานที่ดำเนินการอยู่ในขณะนี้และเมื่อเร็วๆ นี้ ซึ่งอาจทำให้แอปพลิเคชันสามารถค้นข้อมูลได้ว่าอุปกรณ์นี้ใช้แอปพลิเคชันใดบ้าง"</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"โต้ตอบระหว่างผู้ใช้"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"อนุญาตให้แอปพลิเคชันทำงานได้กับผู้ใช้หลายรายบนอุปกรณ์นี้ แอปพลิเคชันที่เป็นอันตรายอาจใช้การทำงานนี้ในการบุกรุกการป้องกันระหว่างผู้ใช้"</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"ใบอนุญาตฉบับเต็มสำหรับการโต้ตอบระหว่างผู้ใช้"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"อนุญาตให้แอปพลิเคชันเปลี่ยนแปลงประวัติหรือบุ๊กมาร์กของเบราว์เซอร์ที่จัดเก็บไว้ในโทรศัพท์ ซึ่งทำให้แอปพลิเคชันสามารถลบหรือเปลี่ยนข้อมูลเบราว์เซอร์ได้ หมายเหตุ: การอนุญาตนี้อาจไม่สามารถใช้งานได้กับเบราว์เซอร์ของบุคคลที่สามหรือแอปพลิเคชันอื่นๆ ที่มีความสามารถในการเรียกดูบนเว็บ"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"ตั้งปลุก"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"อนุญาตให้แอปพลิเคชันตั้งเวลาปลุกในแอปพลิเคชันนาฬิกาปลุกที่ติดตั้ง แอปพลิเคชันนาฬิกาปลุกบางรายการอาจไม่ใช้คุณลักษณะนี้"</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"เขียนข้อความเสียง"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"อนุญาตให้แอปแก้ไขและนำข้อความออกจากกล่องข้อความเสียงของคุณ"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"เพิ่มข้อวามเสียง"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"อนุญาตให้แอปพลิเคชันเพิ่มข้อความลงในกล่องข้อความเสียงของคุณ"</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"อ่านข้อความเสียง"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"อนุญาตให้แอปอ่านข้อความเสียงของคุณได้"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"แก้ไขการอนุญาตเกี่ยวกับการระบุตำแหน่งทางภูมิศาสตร์ของเบราว์เซอร์"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"อนุญาตให้แอปพลิเคชันแก้ไขการอนุญาตตำแหน่งทางภูมิศาสตร์ของเบราว์เซอร์ แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ในการส่งข้อมูลตำแหน่งไปยังเว็บไซต์ต่างๆ ได้ตามต้องการ"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ยืนยันแพ็กเกจ"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"อนุญาตให้แอปพลิเคชันผูกกับบริการของตัวแทนที่เชื่อถือได้"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"โต้ตอบกับการอัปเดตและระบบการกู้คืน"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"ช่วยให้แอปพลิเคชันสามารถโต้ตอบกับระบบการกู้คืนและการอัปเดตระบบ"</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"สร้างเซสชันการฉายภาพสื่อ"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"อนุญาตให้แอปพลิเคชันสร้างเซสชันการฉายภาพสื่อ เซสชันดังกล่าวช่วยให้แอปพลิเคชันสามารถจับภาพหน้าจอและเนื้อหาเสียงได้ แอปทั่วไปไม่จำเป็นต้องใช้"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"แตะสองครั้งเพื่อควบคุมการซูม"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ไม่สามารถเพิ่มวิดเจ็ต"</string>
<string name="ime_action_go" msgid="8320845651737369027">"ไป"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"แก้ไข"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"คำเตือนการใช้ข้อมูล"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"แตะเพื่อดูการใช้งานและการตั้งค่า"</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"ข้อมูล 2G-3G ปิดอยู่"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"ข้อมูล 4G ปิดอยู่"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"ข้อมูลมือถือปิดอยู่"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"ข้อมูล Wi-Fi ปิดอยู่"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"ถึงขีดจำกัดแล้ว"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"เกินขีดจำกัดข้อมูล 2G - 3G"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"เกินขีดจำกัดของข้อมูล 4G"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"ถึงขีดจำกัดข้อมูลมือถือแล้ว"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"เกินขีดจำกัดข้อมูล WiFi แล้ว"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> เกินขีดจำกัดที่ระบุไว้"</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"จำกัดข้อมูลแบ็กกราวด์"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ระบบ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"เสียงบลูทูธ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"การแสดงผลแบบไร้สาย"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"เอาต์พุตสื่อ"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"เชื่อมต่อกับอุปกรณ์"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"ส่งหน้าจอไปยังอุปกรณ์"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"กำลังค้นหาอุปกรณ์…"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 377708c..bb74ca3 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Pinapayagan ang app na makatanggap at makapagproseso ng mga mensahe ng Bluetooth MAP. Nangangahulugan ito na maaaring subaybayan o i-delete ng app ang mga mensaheng ipinapadala sa iyong device nang hindi ipinapakita ang mga ito sa iyo."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"bawiin ang tumatakbong apps"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Pinapayagan ang app na kumuha ng impormasyon tungkol sa mga kasalukuyan at kamakailang gumaganang gawain. Maaari nitong payagan ang app na tumuklas ng impormasyon tungkol sa kung aling mga application ang ginagamit sa device."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"makipag-ugnayan sa kabuuan ng mga user"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Binibigyang-daan ang app upang magsagawa ng mga pagkilos sa kabuuan ng iba\'t ibang mga user sa device. Maaari itong gamitin ng nakakahamak na apps upang lumabag sa proteksyon sa pagitan ng mga user."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"ganap na lisensya upang makipag-ugnayan sa kabuuan ng mga user"</string>
@@ -719,10 +723,8 @@
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Nagbibigay-daan sa may-ari na mapailalim sa interface sa tuktok na antas ng isang serbisyo ng notification listener. Hindi dapat kailanganin para sa karaniwang apps kahit kailan."</string>
<string name="permlab_bindConditionProviderService" msgid="1180107672332704641">"i-bind sa isang serbisyo sa pagbibigay ng kundisyon"</string>
<string name="permdesc_bindConditionProviderService" msgid="1680513931165058425">"Nagbibigay-daan sa naghahawak na i-bind ang top-level na interface ng isang serbisyo sa pagbibigay ng kundisyon. Hindi kailanman dapat kailanganin ng mga normal na app."</string>
- <!-- no translation found for permlab_bindMediaRouteService (6637740382272686835) -->
- <skip />
- <!-- no translation found for permdesc_bindMediaRouteService (6436655024972496687) -->
- <skip />
+ <string name="permlab_bindMediaRouteService" msgid="6637740382272686835">"mag-bind sa isang serbisyo ng media route"</string>
+ <string name="permdesc_bindMediaRouteService" msgid="6436655024972496687">"Nagbibigay-daan sa may-hawak na mag-bind sa top-level na interface ng isang serbisyo ng media route. Hindi kailanman dapat kailanganin ng mga normal na app."</string>
<string name="permlab_bindDreamService" msgid="4153646965978563462">"sumailalim sa isang serbisyo ng dream"</string>
<string name="permdesc_bindDreamService" msgid="7325825272223347863">"Pinapayagan ang may-ari na sumailalim sa interface ng serbisyo ng dream na nasa nangungunang antas. Hindi kailanman dapat na kailanganin para sa mga normal na app."</string>
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"paganahin ang app ng configuration na ibinigay ng carrier"</string>
@@ -1008,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Pinapayagan ang app na baguhin ang kasaysayan o mga bookmark ng Browser na naka-imbak sa iyong telepono. Maaari nitong payagan ang app na burahin o baguhin ang data ng Browser. Tandaan: hindi maaaring ipatupad ang pahintulot na ito ng mga third-party na browser o iba pang mga application na may mga kakayahan sa pagba-browse sa web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"magtakda ng alarm"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Pinapayagan ang app na magtakda ng alarm sa isang naka-install na app ng alarm clock. Maaaring hindi ipatupad ng ilang apps ng alarm clock ang tampok na ito."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"sumulat ng mga voicemail"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Pinapayagan ang app na magbago at mag-alis ng mga mensahe sa iyong inbox ng voicemail."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"magdagdag ng voicemail"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Pinapayagan ang app na magdagdag ng mga mensahe sa iyong inbox ng voicemail."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"basahin ang voicemail"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Pinapayagan ang app na basahin ang iyong mga voicemail."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"baguhin ang mga pahintulot ng geolocation ng Browser"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Pinapayagan ang app na baguhin ang mga pahintulot sa geolocation ng Browser. Maaari itong gamitin ng nakakahamak na apps upang payagan ang pagpapadala ng impormasyon ng lokasyon sa mga hindi tukoy na web site."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"i-verify ang mga package"</string>
@@ -1185,20 +1183,13 @@
<string name="capital_on" msgid="1544682755514494298">"I-ON"</string>
<string name="capital_off" msgid="6815870386972805832">"I-OFF"</string>
<string name="whichApplication" msgid="4533185947064773386">"Kumpletuhin ang pagkilos gamit ang"</string>
- <!-- no translation found for whichApplicationNamed (8260158865936942783) -->
- <skip />
- <!-- no translation found for whichViewApplication (3272778576700572102) -->
- <skip />
- <!-- no translation found for whichViewApplicationNamed (2286418824011249620) -->
- <skip />
- <!-- no translation found for whichEditApplication (144727838241402655) -->
- <skip />
- <!-- no translation found for whichEditApplicationNamed (1775815530156447790) -->
- <skip />
- <!-- no translation found for whichSendApplication (6902512414057341668) -->
- <skip />
- <!-- no translation found for whichSendApplicationNamed (2799370240005424391) -->
- <skip />
+ <string name="whichApplicationNamed" msgid="8260158865936942783">"Tapusin ang pagkilos gamit ang %1$s"</string>
+ <string name="whichViewApplication" msgid="3272778576700572102">"Buksan gamit ang"</string>
+ <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Buksan gamit ang %1$s"</string>
+ <string name="whichEditApplication" msgid="144727838241402655">"I-edit gamit ang"</string>
+ <string name="whichEditApplicationNamed" msgid="1775815530156447790">"I-edit gamit ang %1$s"</string>
+ <string name="whichSendApplication" msgid="6902512414057341668">"Ibahagi gamit ang"</string>
+ <string name="whichSendApplicationNamed" msgid="2799370240005424391">"Ibahagi gamit ang %1$s"</string>
<string name="whichHomeApplication" msgid="4616420172727326782">"Pumili ng home app"</string>
<string name="alwaysUse" msgid="4583018368000610438">"Gamitin bilang default para sa pagkilos na ito."</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"I-clear ang default sa mga setting ng System > Apps > Na-download."</string>
@@ -1301,12 +1292,10 @@
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"Palaging Payagan"</string>
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"Huwag kailanman Payagan"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"Naalis ang SIM card"</string>
- <!-- no translation found for sim_removed_message (5450336489923274918) -->
- <skip />
+ <string name="sim_removed_message" msgid="5450336489923274918">"Magiging unavailable ang cellular network hanggang mag-restart ka nang may nakalagay na wastong SIM card."</string>
<string name="sim_done_button" msgid="827949989369963775">"Tapos na"</string>
<string name="sim_added_title" msgid="3719670512889674693">"Idinagdag ang SIM card"</string>
- <!-- no translation found for sim_added_message (7797975656153714319) -->
- <skip />
+ <string name="sim_added_message" msgid="7797975656153714319">"I-restart ang iyong device upang ma-access ang cellular network."</string>
<string name="sim_restart_button" msgid="4722407842815232347">"I-restart"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"Magtakda ng oras"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"Itakda ang petsa"</string>
@@ -1401,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Pinapayagan ang isang application na sumailalim sa isang serbisyo ng trust agent."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Makipag-ugnay sa system ng pag-update at pagbawi"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Binibigyang-daan ang isang application na makipag-ugnay sa system ng pagbawi at mga pag-update ng system."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Gumawa ng mga media projection session"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Nagbibigay-daan sa isang application upang gumawa ng mga media projection session. Makakapagbigay ang mga session na ito ng kakayahan sa mga application na ma-capture ang mga nilalaman ng display at audio. Hindi kailanman dapat kailanganin ng mga normal na app."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Pindutin nang dalawang beses para sa pagkontrol ng zoom"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Hindi maidagdag ang widget."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Pumunta"</string>
@@ -1529,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"I-edit"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Babala sa paggamit ng data"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Pindutin upang tingnan ang paggamit at mga setting."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"Naka-off ang 2G-3G data"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"Naka-off ang 4G data"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Naka-off ang cellular data"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Naka-off ang Wi-Fi data"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Naabot na ang limitasyon"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"lumampas sa 2G-3G na limitasyon ng data"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Lumampas sa 4G na limitasyon ng data"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Lampas na sa cellular data limit"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Lumampas sa limitasyon ng data ng Wi-Fi"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"Lampas ng <xliff:g id="SIZE">%s</xliff:g> sa tinukoy na limitasyon."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Pinaghihigpitan ang data ng background"</string>
@@ -1578,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio sa Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wireless display"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Output ng media"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Kumonekta sa device"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"I-cast ang screen sa device"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Naghahanap ng mga device…"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 7433771..927e7cb 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Uygulamanın Bluetooth MAP iletilerini alıp işlemesine izin verir. Bu izin, uygulamanın cihazınıza gönderilen iletileri izleyebileceği veya size göstermeden silebileceği anlamına gelir."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"çalışan uygulamaları al"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Uygulamaya o anda ve son çalışan görevler hakkında bilgi alma izni verir. Bu izin, uygulamanın cihaz tarafından kullanılan uygulamalar hakkında bilgi elde etmesine olanak sağlayabilir."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"kullanıcılar arasında etkileşim kurma"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Uygulamaya cihazdaki farklı kullanıcılar arasında işlem gerçekleştirme izni verir. Kötü amaçlı uygulamalar bu izinle kullanıcılar arasındaki korumayı ihlal edebilir."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"kullanıcılar arasında etkileşim kurmak için tam izin"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Uygulamaya Tarayıcı geçmişini ve telefonunuzda depolanan yer işaretlerini değiştirme izni verir. Bu izin, uygulamanın Tarayıcı geçmişini silmesine ve değiştirmesine olanak sağlar. Not: Bu izin, üçüncü taraf cihazlar veya Web\'e göz atma işlevine sahip diğer uygulamalar tarafından kullanılmayabilir."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"alarm ayarla"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Uygulamaya, çalar saat uygulamasının alarmını ayarlama izni verir. Bazı çalar saat uygulamaları bu özelliği uygulayamayabilir."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"sesli mesaj yaz"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Uygulamanın sesli mesaj gelen kutunuzdaki mesajları değiştirmesine ve kaldırmasına izin verir."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"sesli mesaj ekle"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Uygulamaya, sesli mesaj gelen kutunuza mesaj ekleme izni verir."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"sesli mesaj oku"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Uygulamanın sesli mesajlarınızı okumasına izin verir."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Tarayıcı\'nın coğrafi konum izinlerini değiştir"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Uygulamaya, Tarayıcı\'nın coğrafi konum izinlerini değiştirme izni verir. Kötü amaçlı uygulamalar keyfi web sitelerine konum bilgisi gönderilmesini sağlamak için bunu kullanabilirler."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"paketleri doğrula"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Bir uygulamanın, güven aracı hizmetine bağlanmasına izin verir."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Güncelleme ve kurtarma sistemiyle etkileşim kur"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Bir uygulamaya, kurtarma sistemi ve sistem güncellemeriyle etkileşim kurma izni verir."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Medya yansıtma oturumları oluştur"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Bir uygulamanın medya yansıtma oturumları oluşturmasına izin verir. Bu oturumlar, uygulamaların görüntü ve ses içeriklerini kaydetmesine olanak sağlar. Normal uygulamalar için hiçbir zaman gerekli olmaz."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Yakınlaştırma denetimi için iki kez dokunun"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget eklenemedi."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Git"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Düzenle"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Veri kullanım uyarısı"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Kullanımı ve ayarları görmek için dokunun."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G-3G veri kullanımı kapalı"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G veri kullanımı kapalı"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Hücresel veri kapalı"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Kablosuz veri kapalı"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Sınıra ulaşıldı"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G-3G veri limiti aşıldı"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4G veri limiti aşıldı"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Hücresel veri sınırı aşıldı"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Kablosuz veri limiti aşıldı"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g>, belirlenen limiti aşıyor."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Arka plan verileri kısıtlı"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth ses"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Kablosuz ekran"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Medya çıkışı"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Cihaza bağlanın"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Ekranı cihaza yayınlayın"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Cihaz aranıyor…"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index eeb6e15..3a217be 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Дозволяє додаткові отримувати й обробляти повідомлення Bluetooth MAP. Це означає, що додаток може відстежувати чи видаляти повідомлення, надіслані на ваш пристрій, навіть не показуючи їх вам."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"отримувати запущені програми"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Дозволяє програмі отримувати інформацію про поточні й останні запущені завдання. Це може дозволити програмі виявляти інформацію про програми, які використовуються на пристрої."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"взаємодіяти між користувачами"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Дозволяє програмі виконувати дії щодо різних користувачів на пристрої. Шкідливі програми можуть використовувати це для порушення захисту окремих користувачів."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"повна ліцензія на взаємодію між користувачами"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Дозволяє програмі змінювати історію чи закладки веб-переглядача, збережені у вашому телефоні. Це може дозволити програмі стирати чи змінювати дані веб-переглядача. Зауважте: цей дозвіл не може застосовуватися веб-переглядачами третіх сторін або іншими програмами з можливостями веб-перегляду."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"установлювати будильник"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Дозволяє програмі налаштовувати сигнал у встановленій програмі будильника. У деяких програмах будильника ця функція може не застосовуватися."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"записувати голосові повідомлення"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Додаток може змінювати голосові повідомлення та видаляти їх із папки \"Вхідні\"."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"додавати голосову пошту"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Дозволяє програмі додавати повідомлення в папку \"Вхідні\" голосової пошти."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"читати голосові повідомлення"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Додаток може читати голосові повідомлення."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"змінювати дозволи географічного місцезнаходження у веб-переглядачі"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Дозволяє програмі змінювати дозволи географічного місцезнаходження у веб-переглядачі. Шкідливі програми можуть використовувати це, щоб дозволяти надсилати інформацію про місцезнаходження довільним веб-сайтам."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"перевіряти пакети"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Дозволяє додатку прив’язуватися до служби довірчих агентів."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Взаємодіяти з оновленнями системи та системою відновлення."</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Дозволяє додатку взаємодіяти із системою відновлення й оновленнями системи."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Створювати сеанси трансляції вмісту"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Додаток може створювати сеанси трансляції вмісту. Під час цих сеансів додаток зможе отримати доступ до аудіо й зображення на екрані. Не використовується звичайними додатками."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Двічі торкніться, щоб керувати масштабом"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Не вдалося додати віджет."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Йти"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Редагувати"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Застереження про використ. даних"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Торкн.,щоб див. викор. і налашт."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"Дані 2G–3G вимкнено"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"Дані 4G вимкнено"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Мобільні дані вимкнено"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Дані Wi-Fi вимкнено"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Перевищено ліміт"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Перевищено ліміт даних 2G–3G"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Перевищено ліміт даних 4G"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Перевищено ліміт мобільних даних"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Перевищено ліміт даних Wi-Fi"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> – понад указаний ліміт."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Викор-ня фонових даних обмежено"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Система"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Аудіо Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Бездротовий екран"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Вивід медіа-даних"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Під’єднатися до пристрою"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Транслювати екран на пристрій"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Пошук пристроїв…"</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 7da441c..216a726 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"ایپ کو بلوتوٹھ MAP پیغامات وصول اور ان پر کارروائی کرنے کی اجازت دیتی ہے۔ اس کا مطلب یہ ہے کہ ایپ آپ کے آلہ پر ارسال کردہ پیغامات آپ کو دکھائے بغیر ان پر نگاہ رکھ یا انہیں حذف کرسکتی ہے۔"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"چل رہی ایپس کی بازیافت کریں"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"ایپ کو موجودہ اور حالیہ چل رہے ٹاسکس کے بارے میں معلومات بازیافت کرنے کی اجازت دیتا ہے۔ یہ ایپ کو اس بارے میں معلومات دریافت کرنے کی اجازت دے سکتا ہے کہ آلہ پر کون سی ایپلیکیشنز استعمال کی جاتی ہیں۔"</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"سبھی صارفین کے ساتھ تعامل کریں"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"ایپ کو آلے پر موجود مختلف صارفین کے بیچ کارروائیاں انجام دینے کی اجازت دیتا ہے۔ نقصان دہ ایپس صارفین کے مابین تحفظ کی خلاف ورزی کرنے کیلئے اسے استعمال کرسکتی ہیں۔"</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"سبھی صارفین کے ساتھ تعامل کرنے کیلئے مکمل لائسنس"</string>
@@ -720,7 +724,7 @@
<string name="permlab_bindConditionProviderService" msgid="1180107672332704641">"شرط فراہم کرنے والی ایک سروس کے پابند بنیں"</string>
<string name="permdesc_bindConditionProviderService" msgid="1680513931165058425">"حامل کو شرط فراہم کنندہ کی سروس کے اعلی سطحی انٹرفیس کا پابند ہونے کی اجازت دیتا ہے۔ عام ایپس کیلئے کبھی بھی اس کی ضرورت نہيں ہونی چاہئے۔"</string>
<string name="permlab_bindMediaRouteService" msgid="6637740382272686835">"میڈیا روٹ سروس کا پابند بنیں"</string>
- <string name="permdesc_bindMediaRouteService" msgid="6436655024972496687">"حامل کو میڈیا روٹ سروس کے اعلی سطحی انٹرفیس کا پابند کرنے کی اجازت دیتا ہے۔ معمول کے ایپس کیلئے کبھی درکار نہیں ہونا چاہئے۔"</string>
+ <string name="permdesc_bindMediaRouteService" msgid="6436655024972496687">"حامل کو میڈیا روٹ سروس کے اعلی سطحی انٹرفیس کا پابند کرنے کی اجازت دیتا ہے۔ معمول کی ایپس کیلئے کبھی درکار نہیں ہونا چاہئے۔"</string>
<string name="permlab_bindDreamService" msgid="4153646965978563462">"ایک ڈریم سروس کا پابند بنیں"</string>
<string name="permdesc_bindDreamService" msgid="7325825272223347863">"حامل کو ڈریم سروس کے اعلی سطحی انٹرفیس کا پابند ہونے کی اجازت دیتی ہے۔ عام ایپس کیلئے کبھی بھی اس کی ضرورت نہيں ہونی چاہیے۔"</string>
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"کیریئر کے ذریعے فراہم کردہ کنفگریشن ایپ طلب کریں"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"ایپ کو آپ کے فون پر اسٹور کردہ براؤزر کی سرگزشت یا بک مارکس میں ترمیم کرنے کی اجازت دیتا ہے۔ یہ ایپ کو براؤزر کا ڈیٹا مٹانے یا اس میں ترمیم کرنے کی اجازت دے سکتا ہے۔ نوٹ: یہ اجازت تیسرے فریق کے براؤزرز یا ویب براؤزنگ کی لیاقتوں والی دیگر ایپلیکیشنز کے ذریعہ نافذ نہیں کی جاسکتی ہے۔"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"ایک الارم سیٹ کریں"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"ایپ کو ایک انسٹال کردہ الارم گھڑی کی ایپ میں ایک الارم سیٹ کرنے کی اجازت دیتا ہے۔ الارم گھڑی کی کچھ ایپس اس خصوصیت کو نافذ نہیں کر سکتی ہیں۔"</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"صوتی میلز لکھیں"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"ایپ کو پیغامات میں ترمیم کرنے اور ان کو آپ کے صوتی میل ان باکس سے ہٹانے کی اجازت دیتا ہے۔"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"صوتی میل شامل کریں"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"ایپ کو آپ کے صوتی میل کے ان باکس میں پیغامات شامل کرنے کی اجازت دیتا ہے۔"</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"صوتی میل پڑھیں"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"اپنے صوتی میلز پڑھنے کیلئے ایپ کو اجازت دیتا ہے۔"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"براؤزر کی جغرافیائی مقام کی اجازتوں میں ترمیم کریں"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ایپ کو براؤزر کی جغرافیائی مقام کی اجازتوں میں ترمیم کرنے کی اجازت دیتا ہے۔ نقصان دہ ایپس متنازعہ ویب سائٹس پر مقام کی معلومات بھیجنے کی اجازت دینے کیلئے اس کا استعمال کر سکتی ہیں۔"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"پیکیجز کی توثیق کریں"</string>
@@ -1292,10 +1292,10 @@
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"ہمیشہ اجازت دیں"</string>
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"کبھی بھی اجازت نہ دیں"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"SIM کارڈ ہٹا دیا گیا"</string>
- <string name="sim_removed_message" msgid="5450336489923274918">"سیلولر نیٹ ورک اس وقت تک دستیاب نہیں ہو گا جب تک آپ ایک درست SIM داخل کرکے اسے دوبارہ چالو نہ کریں۔"</string>
+ <string name="sim_removed_message" msgid="5450336489923274918">"سیلولر نیٹ ورک اس وقت تک دستیاب نہیں ہو گا جب تک آپ ایک درست SIM داخل کرکے اسے دوبارہ سٹارٹ نہ کریں۔"</string>
<string name="sim_done_button" msgid="827949989369963775">"ہو گیا"</string>
<string name="sim_added_title" msgid="3719670512889674693">"SIM شامل کیا گیا"</string>
- <string name="sim_added_message" msgid="7797975656153714319">"سیلولر نیٹ ورک تک رسائی کیلئے اپنا آلہ دوبارہ چالو کریں۔"</string>
+ <string name="sim_added_message" msgid="7797975656153714319">"سیلولر نیٹ ورک تک رسائی کیلئے اپنا آلہ دوبارہ سٹارٹ کریں۔"</string>
<string name="sim_restart_button" msgid="4722407842815232347">"دوبارہ شروع کریں"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"وقت سیٹ کریں"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"تاریخ سیٹ کریں"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"ایک ایپلیکیشن کو ایک ٹرسٹ ایجنٹ سروس کا پابند ہونے دیتا ہے۔"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"اپ ڈیٹ اور بازیابی سسٹم کے ساتھ تعامل کریں"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"کسی ایپ کو بازیابی سسٹم اور بازیابی اپ ڈیٹس کے ساتھ تعامل کرنے کی اجازت دیتا ہے۔"</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"میڈیا پروجیکشن سیشنز بنائیں"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"ایک ایپلیکیشن کو میڈیا پروجیکشن سیشنز بنانے کی اجازت دیتی ہے۔ یہ سیشنز ایپلیکیشنز کو ڈسپلے اور آڈیو مواد کو کیپچر کرنے کی اہلیت فراہم کر سکتے ہیں۔ عام ایپس کو کبھی بھی ان کی ضرورت نہیں ہونی چاہیے۔"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"زوم کے کنٹرول کیلئے دو بار ٹچ کریں"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"ویجٹس کو شامل نہیں کرسکا۔"</string>
<string name="ime_action_go" msgid="8320845651737369027">"جائیں"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"ترمیم کریں"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"ڈیٹا کے استعمال کی وارننگ"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"استعمال و ترتیبات دیکھنے کیلئے ٹچ کریں۔"</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G-3G ڈیٹا آف ہے"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G ڈیٹا آف ہے"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"سیلولر ڈیٹا آف ہے"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi ڈیٹا آف ہے"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"حد تک پہنچ گیا"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G-3G ڈیٹا حد سے متجاوز ہو گیا"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4G ڈیٹا حد سے متجاوز ہو گیا"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"سیلولر ڈیٹا کی حد سے تجاوز کرگیا"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Wi-Fi ڈیٹا حد سے متجاوز ہو گیا"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> متعینہ حد سے زیادہ ہے۔"</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"پس منظر ڈیٹا محدود ہے"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"سسٹم"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"بلوٹوتھ آڈیو"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"وائرلیس ڈسپلے"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"میڈیا آؤٹ پٹ"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"آلہ سے مربوط ہوں"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"اسکرین کو آلہ پر کاسٹ کریں"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"آلات تلاش کر رہا ہے…"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index c3e7f59..fb0b55fc 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Bluetooth MAP xabarlarni qabul qilish va qayta ishlash. O‘qilmagan xabarlarni kuzatish va o‘chirish."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"ishlab turgan ilovalar to‘g‘risida ma’lumot olish"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Ilovaga hozirda va so‘nggi ishga tushirilgan vazifalar haqida to‘liq ma’lumot olishiga ruxsat beradi. Bu ilovaga qurilmadagi ishlatilayotgan ilovalar haqidagi ma’lumotlarga ega bo‘lishiga ruxsat berishi mumkin."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"foydalanuvchilar o‘rtasida o‘zaro aloqa"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Ilovaga qurilmadagi turli foydalanuvchilarga ta‘sir ko‘rsatadigan amallarni bajarishga ruxsat beradi. Zararli ilovalar bundan foydalanuvchilar o‘rtasidagi himoyani buzishda foydalanishi mumkin."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"foydalanuvchilar o‘rtasidagi o‘zaro aloqa uchun to‘liq litsenziya"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Ilovaga telefoningizga zaxiralangan brauzer tarixi yoki xatcho‘plarini o‘zgartirish uchun ruxsat beradi. Bu ilovaga brauzer ma’lumotlarini o‘zgartirish yoki o‘chirishga ruxsat berishi mumkin. Diqqat qiling: ushbu ruxsat uchinchi taraf brauzerlari yoki internetni ko‘rsatish qobiliyatiga ega boshqa ilovalardan talab qilinmasligi mumkin."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"signal o‘rnatish"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Ilova uyg‘otkichni sozlashi mumkin. Ba’zi soat ilovalari ushbu funksiyani qo‘llab-quvvatlamasligi mumkin."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"ovozli pochta xabarlarini yozish"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Ilovaga ovozli pochtangizdagi xabarlarni o‘zgartirish va o‘chirish uchun ruxsat beradi."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ovozli xat qo‘shish"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Ilova xabarlarni ovozli pochta qutingizga qo‘shishi mumkin."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"ovozli pochta xabarlarini o‘qish"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Ilovaga ovozli pochta xabarlaringizni o‘qish uchun ruxsat beradi."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"brauzerdagi geolokatsiya ma’lumotlariga kirish huquqini o‘zgartirish"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Ilova brauzerdagi geolokatsiya ma’lumotlariga kirish sozlamalarini o‘zgartirishi mumkin. Zararli dasturlar uning yordamida joylashuv to‘g‘risidagi ma’lumotlarni boshqa istalgan veb-saytga yuborishi mumkin."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"paketlarni tekshirish"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Ilova ishonchli agentlar xizmatiga ulanishi mumkin."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Tizimni yangilash va tiklashni birgalikda amalga oshirish"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Dasturga tizimni tiklash va yangilash imkoniyatlari bilan ishlash uchun ruxsat beradi."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Kontentni translatsiya qilish seanslarini yaratish"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Ilovaga kontentni translatsiya qilish seanslarini yaratish uchun ruxsat beradi. Ushbu seanslar yordamida ilovalar qurilma ekranidagi tasvirlar va audio kontentda foydalanish huquqini qo‘lga kiritadi. Oddiy ilovalar uchun talab qilinmaydi."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Masshtabni o‘zgartirish uchun ikki marta bosing"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Vidjet qo‘shilmadi."</string>
<string name="ime_action_go" msgid="8320845651737369027">"O‘tish"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Tahrirlash"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Ma’lumotlardan foydalanish ogohlantirilishi"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Trafik sarfi va sozlamalarni ko‘rish uchun bosing."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G/3G internet o‘chirib qo‘yildi"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G internet o‘chirib qo‘yildi"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Mobil internet o‘chirib qo‘yildi"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi internet o‘chirib qo‘yildi"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Chegaraga yetib keldi"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"2G-3G ma’lumot cheklovdan o‘tdi"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"4G ma’lumot cheklovdan o‘tdi"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Mob. trafik cheg-dan oshib ketdi"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Wi-Fi ma’lumot cheklovdan o‘tdi"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"Chegaradan <xliff:g id="SIZE">%s</xliff:g> oshib ketdi."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Orqa fon ma’lumotlari cheklangan"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Tizim"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Simsiz ekran"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Media chiqish"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Qurilmaga ulanish"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Ekrandagi tasvirni qurilmaga uzatish"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Qurilmalar izlanmoqda..."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 7ef5017..c12ef19 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Cho phép ứng dụng nhận và xử lý tin nhắn MAP qua Bluetooth. Điều này có nghĩa là ứng dụng có thể giám sát hoặc xóa tin nhắn được gửi đến thiết bị của bạn mà không hiển thị chúng cho bạn."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"truy xuất các ứng dụng đang chạy"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Cho phép ứng dụng truy xuất thông tin về các công việc đã và đang chạy gần đây. Việc này có thể cho phép ứng dụng phát hiện thông tin về những ứng dụng nào đã được sử dụng trên thiết bị."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"tương tác giữa người dùng"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Cho phép ứng dụng thực hiện hành động giữa những người dùng khác trên thiết bị. Ứng dụng độc hại có thể sử dụng quyền này để vi phạm khả năng bảo vệ giữa người dùng."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"cấp phép đầy đủ để tương tác giữa người dùng"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Cho phép ứng dụng sửa đổi lịch sử hoặc dấu trang của Trình duyệt được lưu trữ trên điện thoại của bạn. Việc này có thể cho phép ứng dụng xóa hoặc sửa đổi dữ liệu của Trình duyệt. Lưu ý: quyền này có thể không được thực thi bởi các trình duyệt của bên thứ ba hoặc các ứng dụng khác có khả năng duyệt web."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"đặt báo thức"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Cho phép ứng dụng đặt báo thức trong ứng dụng đồng hồ báo thức được cài đặt. Một số ứng dụng đồng hồ báo thức có thể không thực thi tính năng này."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"viết thư thoại"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Cho phép ứng dụng sửa đổi và xóa thư khỏi hộp thư thoại đến của bạn."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"thêm thư thoại"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Cho phép ứng dụng thêm thông báo vào hộp thư thoại đến của bạn."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"đọc thư thoại"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Cho phép ứng dụng đọc thư thoại của bạn."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"sửa đổi các quyền về vị trí địa lý của Trình duyệt"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Cho phép ứng dụng sửa đổi cấp phép vị trí địa lý của Trình duyệt. Ứng dụng độc hại có thể lợi dụng quyền này để cho phép gửi thông tin vị trí tới các trang web tùy ý."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"xác minh gói"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Cho phép ứng dụng liên kết với một dịch vụ của đại lý đáng tin cậy."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Tương tác với hệ thống khôi phục và bản cập nhật"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"Cho phép ứng dụng tương tác với hệ thống khôi phục và bản cập nhật hệ thống."</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"Tạo phiên chiếu phương tiện"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"Cho phép ứng dụng tạo các phiên chiếu phương tiện. Các phiên này có thể cấp cho ứng dụng khả năng chụp màn hình và ghi nội dung âm thanh. Không cần thiết cho các ứng dụng thông thường."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Chạm hai lần để kiểm soát thu phóng"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Không thể thêm tiện ích."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Đến"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Chỉnh sửa"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"Cảnh báo sử dụng dữ liệu"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"Chạm để xem sử dụng và cài đặt."</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"Dữ liệu 2G-3G bị tắt"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"Dữ liệu 4G bị tắt"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"Dữ liệu di động bị tắt"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Dữ liệu Wi-Fi bị tắt"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"Đã đạt giới hạn"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Đã vượt quá g.hạn dữ liệu 2G-3G"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"Đã vượt quá giới hạn dữ liệu 4G"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"Đã vượt quá giới hạn dữ liệu DĐ"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Vượt quá g.hạn d.liệu Wi-Fi"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> vượt quá g.hạn được chỉ định."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Dữ liệu nền bị giới hạn"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Hệ thống"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Âm thanh Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Hiển thị không dây"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Đầu ra phương tiện"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Kết nối với thiết bị"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Truyền màn hình tới thiết bị"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Đang tìm kiếm thiết bị…"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index ac91926..d13d76c 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -55,16 +55,16 @@
<string name="passwordIncorrect" msgid="7612208839450128715">"密码不正确。"</string>
<string name="mmiComplete" msgid="8232527495411698359">"MMI 码已完成。"</string>
<string name="badPin" msgid="9015277645546710014">"您输入的旧 PIN 不正确。"</string>
- <string name="badPuk" msgid="5487257647081132201">"您输入的 PUK 码不正确。"</string>
+ <string name="badPuk" msgid="5487257647081132201">"您输入的PUK码不正确。"</string>
<string name="mismatchPin" msgid="609379054496863419">"您输入的 PIN 码不一致。"</string>
<string name="invalidPin" msgid="3850018445187475377">"输入一个 4 至 8 位数的 PIN 码。"</string>
- <string name="invalidPuk" msgid="8761456210898036513">"请键入至少 8 位数字的 PUK 码。"</string>
- <string name="needPuk" msgid="919668385956251611">"已对 SIM 卡进行 PUK 码锁定。键入 PUK 码将其解锁。"</string>
- <string name="needPuk2" msgid="4526033371987193070">"输入 PUK2 码以解锁 SIM 卡。"</string>
- <string name="enablePin" msgid="209412020907207950">"失败,请启用 SIM/RUIM 卡锁定设置。"</string>
+ <string name="invalidPuk" msgid="8761456210898036513">"请输入至少8位数字的PUK码。"</string>
+ <string name="needPuk" msgid="919668385956251611">"已对SIM卡进行PUK码锁定。键入PUK码将其解锁。"</string>
+ <string name="needPuk2" msgid="4526033371987193070">"输入PUK2码以解锁SIM卡。"</string>
+ <string name="enablePin" msgid="209412020907207950">"失败,请启用SIM/RUIM 卡锁定设置。"</string>
<plurals name="pinpuk_attempts">
- <item quantity="one" msgid="6596245285809790142">"您还有<xliff:g id="NUMBER">%d</xliff:g>次尝试机会。如果仍然失败,SIM 卡将被锁定。"</item>
- <item quantity="other" msgid="7530597808358774740">"您还有<xliff:g id="NUMBER">%d</xliff:g>次尝试机会。如果仍然失败,SIM 卡将被锁定。"</item>
+ <item quantity="one" msgid="6596245285809790142">"您还有<xliff:g id="NUMBER">%d</xliff:g>次尝试机会。如果仍然失败,SIM卡将被锁定。"</item>
+ <item quantity="other" msgid="7530597808358774740">"您还有<xliff:g id="NUMBER">%d</xliff:g>次尝试机会。如果仍然失败,SIM卡将被锁定。"</item>
</plurals>
<string name="imei" msgid="2625429890869005782">"IMEI"</string>
<string name="meid" msgid="4841221237681254195">"MEID"</string>
@@ -120,7 +120,7 @@
<string name="roamingTextSearching" msgid="8360141885972279963">"正在搜索服务"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:无法转接"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
- <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="TIME_DELAY">{2}</xliff:g> 秒后<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
+ <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="TIME_DELAY">{2}</xliff:g>秒后<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:无法转接"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:无法转接"</string>
<string name="fcComplete" msgid="3118848230966886575">"功能代码已拨完。"</string>
@@ -188,7 +188,7 @@
<string name="global_action_lockdown" msgid="8751542514724332873">"立即锁定"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
- <string name="android_system_label" msgid="6577375335728551336">"Android 系统"</string>
+ <string name="android_system_label" msgid="6577375335728551336">"Android系统"</string>
<string name="user_owner_label" msgid="6465364741001216388">"个人应用"</string>
<string name="managed_profile_label" msgid="6260850669674791528">"工作"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"需要您付费的服务"</string>
@@ -287,17 +287,21 @@
<string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"发送“通过信息回复”事件"</string>
<string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"允许应用向其他信息应用发送请求,以便处理来电的“通过信息回复”事件。"</string>
<string name="permlab_readSms" msgid="8745086572213270480">"读取您的讯息(短信或彩信)"</string>
- <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"允许该应用读取您平板电脑或 SIM 卡上存储的短信。此权限可让该应用读取所有短信,而不考虑短信内容或机密性。"</string>
- <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"允许该应用读取您手机或 SIM 卡上存储的短信。此权限可让该应用读取所有短信,而不考虑短信内容或机密性。"</string>
+ <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"允许该应用读取您平板电脑或SIM卡上存储的短信。此权限可让该应用读取所有短信,而不考虑短信内容或机密性。"</string>
+ <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"允许该应用读取您手机或SIM卡上存储的短信。此权限可让该应用读取所有短信,而不考虑短信内容或机密性。"</string>
<string name="permlab_writeSms" msgid="3216950472636214774">"编辑您的讯息(短信或彩信)"</string>
- <string name="permdesc_writeSms" product="tablet" msgid="5160413947794501538">"允许应用对平板电脑或 SIM 卡上存储的短信执行写入操作。恶意应用可能会删除您的短信。"</string>
- <string name="permdesc_writeSms" product="default" msgid="7268668709052328567">"允许应用对手机或 SIM 卡上存储的短信执行写入操作。恶意应用可能会删除您的短信。"</string>
+ <string name="permdesc_writeSms" product="tablet" msgid="5160413947794501538">"允许应用对平板电脑或SIM卡上存储的短信执行写入操作。恶意应用可能会删除您的短信。"</string>
+ <string name="permdesc_writeSms" product="default" msgid="7268668709052328567">"允许应用对手机或SIM卡上存储的短信执行写入操作。恶意应用可能会删除您的短信。"</string>
<string name="permlab_receiveWapPush" msgid="5991398711936590410">"接收讯息 (WAP)"</string>
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"允许该应用接收和处理 WAP 消息。此权限包括监视发送给您的消息或删除发送给您的消息而不向您显示的功能。"</string>
<string name="permlab_receiveBluetoothMap" msgid="7593811487142360528">"接收蓝牙信息(MAP)"</string>
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"允许此应用接收和处理蓝牙MAP信息。这意味着,该应用可能会监视发送或到您设备的信息,在您阅读发送到您设备的信息之前擅自删除信息。"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"检索正在运行的应用"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"允许该应用检索近期运行的和当前正在运行的任务的相关信息。此权限可让该应用了解设备上使用了哪些应用。"</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"用户间互动"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"允许该应用在设备上跨多个用户执行操作。恶意应用可能会借此破坏用户之间的保护措施。"</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"完全允许在用户之间进行互动"</string>
@@ -643,8 +647,8 @@
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"允许应用更改网络连接的状态。"</string>
<string name="permlab_changeTetherState" msgid="5952584964373017960">"更改网络共享连接"</string>
<string name="permdesc_changeTetherState" msgid="1524441344412319780">"允许应用更改绑定网络连接的状态。"</string>
- <string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"更改后台数据使用设置"</string>
- <string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"允许应用更改后台数据使用设置。"</string>
+ <string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"更改后台流量设置"</string>
+ <string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"允许应用更改后台流量设置。"</string>
<string name="permlab_accessWifiState" msgid="5202012949247040011">"查看 WLAN 连接"</string>
<string name="permdesc_accessWifiState" msgid="5002798077387803726">"允许该应用查看 WLAN 网络的相关信息,例如是否启用了 WLAN 以及连接的 WLAN 设备的名称。"</string>
<string name="permlab_changeWifiState" msgid="6550641188749128035">"连接 WLAN 和断开连接"</string>
@@ -870,8 +874,8 @@
<string name="sipAddressTypeOther" msgid="4408436162950119849">"其他"</string>
<string name="quick_contacts_not_available" msgid="746098007828579688">"找不到可用来查看此联系人的应用。"</string>
<string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"输入 PIN 码"</string>
- <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"请输入 PUK 码和新的 PIN 码"</string>
- <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK 码"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"请输入PUK码和新的PIN码"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK码"</string>
<string name="keyguard_password_enter_pin_prompt" msgid="8027680321614196258">"新的 PIN 码"</string>
<string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"触摸可输入密码"</font></string>
<string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"输入密码以解锁"</string>
@@ -894,13 +898,13 @@
<string name="lockscreen_charged" msgid="321635745684060624">"充电完成"</string>
<string name="lockscreen_battery_short" msgid="4477264849386850266">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
<string name="lockscreen_low_battery" msgid="1482873981919249740">"连接您的充电器。"</string>
- <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"没有 SIM 卡"</string>
- <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"平板电脑中没有 SIM 卡。"</string>
- <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"手机中无 SIM 卡"</string>
- <string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"请插入 SIM 卡"</string>
- <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"SIM 卡缺失或无法读取。请插入 SIM 卡。"</string>
- <string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"SIM 卡无法使用。"</string>
- <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"您的 SIM 卡已永久停用。\n请与您的无线服务提供商联系,以便重新获取一张 SIM 卡。"</string>
+ <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"没有SIM卡"</string>
+ <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"平板电脑中没有SIM卡。"</string>
+ <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"手机中无SIM卡"</string>
+ <string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"请插入SIM卡"</string>
+ <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"SIM卡缺失或无法读取。请插入SIM卡。"</string>
+ <string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"SIM卡无法使用。"</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"您的SIM卡已永久停用。\n请与您的无线服务提供商联系,以便重新获取一张SIM卡。"</string>
<string name="lockscreen_transport_prev_description" msgid="201594905152746886">"“上一曲目”按钮"</string>
<string name="lockscreen_transport_next_description" msgid="6089297650481292363">"“下一曲目”按钮"</string>
<string name="lockscreen_transport_pause_description" msgid="7659088786780128001">"“暂停”按钮"</string>
@@ -908,10 +912,10 @@
<string name="lockscreen_transport_stop_description" msgid="4562318378766987601">"“停止”按钮"</string>
<string name="emergency_calls_only" msgid="6733978304386365407">"只能拨打紧急呼救电话"</string>
<string name="lockscreen_network_locked_message" msgid="143389224986028501">"网络已锁定"</string>
- <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"SIM 卡已用 PUK 码锁定"</string>
+ <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"SIM卡已用PUK码锁定。"</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"请参阅《用户指南》或与客服人员联系。"</string>
- <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM 卡被锁定"</string>
- <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"正在解锁 SIM 卡..."</string>
+ <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM卡被锁定"</string>
+ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"正在解锁SIM卡..."</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地绘制了解锁图案。\n\n请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地输入了密码。\n\n请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地输入了 PIN。\n\n请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
@@ -921,7 +925,7 @@
<string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地尝试解锁手机。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,手机将重置为出厂默认设置,所有用户数据将会丢失。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"您已经 <xliff:g id="NUMBER">%d</xliff:g> 次错误地尝试解锁平板电脑。平板电脑现在将重置为出厂默认设置。"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"您已经 <xliff:g id="NUMBER">%d</xliff:g> 次错误地尝试解锁手机。手机现在将重置为出厂默认设置。"</string>
- <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> 秒后重试。"</string>
+ <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g>秒后重试。"</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"忘记了图案?"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"帐户解锁"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"图案尝试次数过多"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"允许该应用修改您手机上存储的浏览器历史记录或浏览器书签。此权限可让该应用清除或修改浏览器数据。请注意:此权限可能不适用于第三方浏览器或具备网页浏览功能的其他应用。"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"设置闹钟"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"允许应用在已安装的闹钟应用中设置闹钟。有些闹钟应用可能无法实现此功能。"</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"写入语音邮件"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"允许应用修改和移除您语音信箱中的语音邮件。"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"添加语音邮件"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"允许应用向您的语音信箱收件箱添加邮件。"</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"读取语音邮件"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"允许应用读取您的语音邮件。"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"修改“浏览器”地理位置的权限"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"允许应用修改“浏览器”的地理位置权限。恶意应用可能借此向任意网站发送位置信息。"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"验证软件包"</string>
@@ -1051,69 +1051,69 @@
<string name="oneMonthDurationPast" msgid="7396384508953779925">"1 个月前"</string>
<string name="beforeOneMonthDurationPast" msgid="909134546836499826">"1 个月前"</string>
<plurals name="num_seconds_ago">
- <item quantity="one" msgid="4869870056547896011">"1 秒前"</item>
- <item quantity="other" msgid="3903706804349556379">"<xliff:g id="COUNT">%d</xliff:g> 秒前"</item>
+ <item quantity="one" msgid="4869870056547896011">"1秒前"</item>
+ <item quantity="other" msgid="3903706804349556379">"<xliff:g id="COUNT">%d</xliff:g>秒前"</item>
</plurals>
<plurals name="num_minutes_ago">
- <item quantity="one" msgid="3306787433088810191">"1 分钟前"</item>
- <item quantity="other" msgid="2176942008915455116">"<xliff:g id="COUNT">%d</xliff:g> 分钟前"</item>
+ <item quantity="one" msgid="3306787433088810191">"1分钟前"</item>
+ <item quantity="other" msgid="2176942008915455116">"<xliff:g id="COUNT">%d</xliff:g>分钟前"</item>
</plurals>
<plurals name="num_hours_ago">
- <item quantity="one" msgid="9150797944610821849">"1 小时前"</item>
- <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> 小时前"</item>
+ <item quantity="one" msgid="9150797944610821849">"1小时前"</item>
+ <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g>小时前"</item>
</plurals>
<plurals name="last_num_days">
- <item quantity="other" msgid="3069992808164318268">"过去 <xliff:g id="COUNT">%d</xliff:g> 天"</item>
+ <item quantity="other" msgid="3069992808164318268">"过去<xliff:g id="COUNT">%d</xliff:g>天"</item>
</plurals>
- <string name="last_month" msgid="3959346739979055432">"上月"</string>
+ <string name="last_month" msgid="3959346739979055432">"上个月"</string>
<string name="older" msgid="5211975022815554840">"往前"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"昨天"</item>
- <item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g> 天前"</item>
+ <item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g>天前"</item>
</plurals>
<plurals name="in_num_seconds">
- <item quantity="one" msgid="2729745560954905102">"1 秒后"</item>
- <item quantity="other" msgid="1241926116443974687">"<xliff:g id="COUNT">%d</xliff:g> 秒后"</item>
+ <item quantity="one" msgid="2729745560954905102">"1秒后"</item>
+ <item quantity="other" msgid="1241926116443974687">"<xliff:g id="COUNT">%d</xliff:g>秒后"</item>
</plurals>
<plurals name="in_num_minutes">
- <item quantity="one" msgid="8793095251325200395">"1 分钟后"</item>
- <item quantity="other" msgid="3330713936399448749">"<xliff:g id="COUNT">%d</xliff:g> 分钟后"</item>
+ <item quantity="one" msgid="8793095251325200395">"1分钟后"</item>
+ <item quantity="other" msgid="3330713936399448749">"<xliff:g id="COUNT">%d</xliff:g>分钟后"</item>
</plurals>
<plurals name="in_num_hours">
- <item quantity="one" msgid="7164353342477769999">"1 小时后"</item>
- <item quantity="other" msgid="547290677353727389">"<xliff:g id="COUNT">%d</xliff:g> 小时后"</item>
+ <item quantity="one" msgid="7164353342477769999">"1小时后"</item>
+ <item quantity="other" msgid="547290677353727389">"<xliff:g id="COUNT">%d</xliff:g>小时后"</item>
</plurals>
<plurals name="in_num_days">
<item quantity="one" msgid="5413088743009839518">"明天"</item>
<item quantity="other" msgid="5109449375100953247">"<xliff:g id="COUNT">%d</xliff:g> 天后"</item>
</plurals>
<plurals name="abbrev_num_seconds_ago">
- <item quantity="one" msgid="1849036840200069118">"1 秒前"</item>
- <item quantity="other" msgid="3699169366650930415">"<xliff:g id="COUNT">%d</xliff:g> 秒前"</item>
+ <item quantity="one" msgid="1849036840200069118">"1秒前"</item>
+ <item quantity="other" msgid="3699169366650930415">"<xliff:g id="COUNT">%d</xliff:g>秒前"</item>
</plurals>
<plurals name="abbrev_num_minutes_ago">
- <item quantity="one" msgid="6361490147113871545">"1 分钟前"</item>
- <item quantity="other" msgid="851164968597150710">"<xliff:g id="COUNT">%d</xliff:g> 分钟前"</item>
+ <item quantity="one" msgid="6361490147113871545">"1分钟前"</item>
+ <item quantity="other" msgid="851164968597150710">"<xliff:g id="COUNT">%d</xliff:g>分钟前"</item>
</plurals>
<plurals name="abbrev_num_hours_ago">
- <item quantity="one" msgid="4796212039724722116">"1 小时前"</item>
- <item quantity="other" msgid="6889970745748538901">"<xliff:g id="COUNT">%d</xliff:g> 小时前"</item>
+ <item quantity="one" msgid="4796212039724722116">"1小时前"</item>
+ <item quantity="other" msgid="6889970745748538901">"<xliff:g id="COUNT">%d</xliff:g>小时前"</item>
</plurals>
<plurals name="abbrev_num_days_ago">
<item quantity="one" msgid="8463161711492680309">"昨天"</item>
- <item quantity="other" msgid="3453342639616481191">"<xliff:g id="COUNT">%d</xliff:g> 天前"</item>
+ <item quantity="other" msgid="3453342639616481191">"<xliff:g id="COUNT">%d</xliff:g>天前"</item>
</plurals>
<plurals name="abbrev_in_num_seconds">
- <item quantity="one" msgid="5842225370795066299">"1 秒后"</item>
- <item quantity="other" msgid="5495880108825805108">"<xliff:g id="COUNT">%d</xliff:g> 秒后"</item>
+ <item quantity="one" msgid="5842225370795066299">"1秒后"</item>
+ <item quantity="other" msgid="5495880108825805108">"<xliff:g id="COUNT">%d</xliff:g>秒后"</item>
</plurals>
<plurals name="abbrev_in_num_minutes">
- <item quantity="one" msgid="562786149928284878">"1 分钟后"</item>
- <item quantity="other" msgid="4216113292706568726">"<xliff:g id="COUNT">%d</xliff:g> 分钟后"</item>
+ <item quantity="one" msgid="562786149928284878">"1分钟后"</item>
+ <item quantity="other" msgid="4216113292706568726">"<xliff:g id="COUNT">%d</xliff:g>分钟后"</item>
</plurals>
<plurals name="abbrev_in_num_hours">
- <item quantity="one" msgid="3274708118124045246">"1 小时后"</item>
- <item quantity="other" msgid="3705373766798013406">"<xliff:g id="COUNT">%d</xliff:g> 小时后"</item>
+ <item quantity="one" msgid="3274708118124045246">"1小时后"</item>
+ <item quantity="other" msgid="3705373766798013406">"<xliff:g id="COUNT">%d</xliff:g>小时后"</item>
</plurals>
<plurals name="abbrev_in_num_days">
<item quantity="one" msgid="2178576254385739855">"明天"</item>
@@ -1135,16 +1135,16 @@
<string name="year" msgid="4001118221013892076">"年"</string>
<string name="years" msgid="6881577717993213522">"年"</string>
<plurals name="duration_seconds">
- <item quantity="one" msgid="6962015528372969481">"1 秒"</item>
- <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> 秒"</item>
+ <item quantity="one" msgid="6962015528372969481">"1秒"</item>
+ <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g>秒"</item>
</plurals>
<plurals name="duration_minutes">
- <item quantity="one" msgid="4915414002546085617">"1 分钟"</item>
- <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> 分钟"</item>
+ <item quantity="one" msgid="4915414002546085617">"1分钟"</item>
+ <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g>分钟"</item>
</plurals>
<plurals name="duration_hours">
- <item quantity="one" msgid="8917467491248809972">"1 小时"</item>
- <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> 小时"</item>
+ <item quantity="one" msgid="8917467491248809972">"1小时"</item>
+ <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g>小时"</item>
</plurals>
<string name="VideoView_error_title" msgid="3534509135438353077">"视频问题"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"抱歉,该视频不适合在此设备上播放。"</string>
@@ -1183,7 +1183,7 @@
<string name="capital_on" msgid="1544682755514494298">"开启"</string>
<string name="capital_off" msgid="6815870386972805832">"关闭"</string>
<string name="whichApplication" msgid="4533185947064773386">"选择要使用的应用:"</string>
- <string name="whichApplicationNamed" msgid="8260158865936942783">"需使用%1$s完成操作"</string>
+ <string name="whichApplicationNamed" msgid="8260158865936942783">"使用%1$s完成操作"</string>
<string name="whichViewApplication" msgid="3272778576700572102">"打开方式"</string>
<string name="whichViewApplicationNamed" msgid="2286418824011249620">"使用%1$s打开"</string>
<string name="whichEditApplication" msgid="144727838241402655">"编辑方式"</string>
@@ -1291,11 +1291,11 @@
<string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"之后,您可以在“设置”>“应用”中更改此设置"</string>
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"始终允许"</string>
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"永不允许"</string>
- <string name="sim_removed_title" msgid="6227712319223226185">"已移除 SIM 卡"</string>
- <string name="sim_removed_message" msgid="5450336489923274918">"您必须先插入有效的SIM卡再重新开机,然后才能使用移动数据网络。"</string>
+ <string name="sim_removed_title" msgid="6227712319223226185">"已移除SIM卡"</string>
+ <string name="sim_removed_message" msgid="5450336489923274918">"您需要先插入有效的SIM卡再重新开机,然后才能使用移动网络。"</string>
<string name="sim_done_button" msgid="827949989369963775">"完成"</string>
- <string name="sim_added_title" msgid="3719670512889674693">"已添加 SIM 卡"</string>
- <string name="sim_added_message" msgid="7797975656153714319">"请重新启动您的设备,以便访问移动数据网络。"</string>
+ <string name="sim_added_title" msgid="3719670512889674693">"已添加SIM卡"</string>
+ <string name="sim_added_message" msgid="7797975656153714319">"请重新启动您的设备,以便使用移动网络。"</string>
<string name="sim_restart_button" msgid="4722407842815232347">"重新启动"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"设置时间"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"设置日期"</string>
@@ -1329,7 +1329,7 @@
<string name="usb_ptp_notification_title" msgid="1960817192216064833">"作为相机连接"</string>
<string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"作为安装程序连接"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"已连接到USB配件"</string>
- <string name="usb_notification_message" msgid="2290859399983720271">"触摸可显示其他 USB 选项。"</string>
+ <string name="usb_notification_message" msgid="2290859399983720271">"触摸可显示其他USB选项。"</string>
<string name="extmedia_format_title" product="nosdcard" msgid="9020092196061007262">"格式化 USB 存储设备吗?"</string>
<string name="extmedia_format_title" product="default" msgid="3648415921526526069">"要格式化 SD 卡吗?"</string>
<string name="extmedia_format_message" product="nosdcard" msgid="3934016853425761078">"您的 USB 存储设备中存储的所有文件都将清除。该操作无法撤消!"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"允许应用绑定至信任的代理服务。"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"与更新和恢复系统互动"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"允许应用与恢复系统和系统更新互动。"</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"创建媒体投影会话"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"允许应用创建媒体投影会话。这些会话可让应用截取显示内容和音频内容。普通应用绝不需要此权限。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"触摸两次可进行缩放控制"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"无法添加小部件。"</string>
<string name="ime_action_go" msgid="8320845651737369027">"开始"</string>
@@ -1516,25 +1514,19 @@
<string name="storage_sd_card" msgid="3282948861378286745">"SD 卡"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB 存储器"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"修改"</string>
- <string name="data_usage_warning_title" msgid="1955638862122232342">"流量使用警告"</string>
+ <string name="data_usage_warning_title" msgid="1955638862122232342">"流量警告"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"触摸可查看使用情况和设置。"</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G-3G数据网络已关闭"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G数据网络已关闭"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"移动数据网络已关闭"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"WLAN数据网络已关闭"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"已达到上限"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"已超出 2G-3G 数据流量限制"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"已超出 4G 数据使用上限"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"已超出移动数据流量上限"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"超出了 WLAN 数据流量上限"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"超出规定上限 <xliff:g id="SIZE">%s</xliff:g>。"</string>
- <string name="data_usage_restricted_title" msgid="5965157361036321914">"后台数据受限制"</string>
+ <string name="data_usage_restricted_title" msgid="5965157361036321914">"后台流量受限制"</string>
<string name="data_usage_restricted_body" msgid="6741521330997452990">"触摸可去除限制。"</string>
<string name="ssl_certificate" msgid="6510040486049237639">"安全证书"</string>
<string name="ssl_certificate_is_valid" msgid="6825263250774569373">"该证书有效。"</string>
@@ -1553,7 +1545,7 @@
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"查看全部"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"选择活动"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"分享方式"</string>
- <string name="list_delimeter" msgid="3975117572185494152">"、 "</string>
+ <string name="list_delimeter" msgid="3975117572185494152">", "</string>
<string name="sending" msgid="3245653681008218030">"正在发送..."</string>
<string name="launchBrowserDefault" msgid="2057951947297614725">"要启动浏览器吗?"</string>
<string name="SetupCallDefault" msgid="5834948469253758575">"要接听电话吗?"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"系统"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"蓝牙音频"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"无线显示"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"媒体输出线路"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"连接到设备"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"将屏幕投射到设备上"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"正在搜索设备…"</string>
@@ -1595,17 +1588,17 @@
<string name="kg_wrong_pin" msgid="1131306510833563801">"PIN 有误"</string>
<string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"请在 <xliff:g id="NUMBER">%1$d</xliff:g> 秒后重试。"</string>
<string name="kg_pattern_instructions" msgid="398978611683075868">"绘制您的图案"</string>
- <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"输入 SIM PIN"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"输入SIM卡PIN码"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"输入 PIN"</string>
<string name="kg_password_instructions" msgid="5753646556186936819">"输入密码"</string>
- <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM 卡已被停用,需要输入 PUK 码才能继续使用。有关详情,请联系您的运营商。"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM卡已被停用,需要输入PUK码才能继续使用。有关详情,请联系您的运营商。"</string>
<string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"请输入所需 PIN 码"</string>
<string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"请确认所需 PIN 码"</string>
- <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"正在解锁 SIM 卡..."</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"正在解锁SIM卡..."</string>
<string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PIN 码有误。"</string>
<string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"请输入 4 至 8 位数的 PIN。"</string>
<string name="kg_invalid_sim_puk_hint" msgid="6025069204539532000">"PUK码应包含8位数字。"</string>
- <string name="kg_invalid_puk" msgid="3638289409676051243">"请重新输入正确的 PUK 码。如果尝试错误次数过多,SIM 卡将永久停用。"</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"请重新输入正确的PUK码。如果尝试错误次数过多,SIM卡将永久停用。"</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN 码不匹配"</string>
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"图案尝试次数过多"</string>
<string name="kg_login_instructions" msgid="1100551261265506448">"要解锁,请登录您的 Google 帐户。"</string>
@@ -1752,25 +1745,16 @@
<string name="item_is_selected" msgid="949687401682476608">"已选择<xliff:g id="ITEM">%1$s</xliff:g>"</string>
<string name="deleted_key" msgid="7659477886625566590">"已删除<xliff:g id="KEY">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge" msgid="2355652472854327647">"工作<xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for lock_to_app_toast (2126866321272822564) -->
- <skip />
+ <string name="lock_to_app_toast" msgid="2126866321272822564">"您目前处于单应用模式。要退出,请触摸并按住“最近用过的应用”按钮"</string>
<string name="lock_to_app_toast_locked" msgid="4229650395479263497">"您目前处于单应用模式。"</string>
<string name="lock_to_app_title" msgid="5895142291937470019">"要使用“单应用模式”吗?"</string>
- <!-- no translation found for lock_to_app_description (2800403592608529611) -->
- <skip />
- <!-- no translation found for lock_to_app_negative (2259143719362732728) -->
- <skip />
+ <string name="lock_to_app_description" msgid="2800403592608529611">"“单应用模式”功能会锁定屏幕,使其只显示一个应用。{\n\n要退出,请触摸并按住“最近用过的应用”按钮。"</string>
+ <string name="lock_to_app_negative" msgid="2259143719362732728">"不用了"</string>
<string name="lock_to_app_positive" msgid="7085139175671313864">"启动"</string>
- <!-- no translation found for lock_to_app_start (3074665051586318340) -->
- <skip />
- <!-- no translation found for lock_to_app_exit (8967089657201849300) -->
- <skip />
- <!-- no translation found for lock_to_app_use_screen_lock (1434584309048590886) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_pin (7908385370846820001) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_pattern (7763071104790758405) -->
- <skip />
- <!-- no translation found for lock_to_app_unlock_password (795224196583495868) -->
- <skip />
+ <string name="lock_to_app_start" msgid="3074665051586318340">"已开启单应用模式"</string>
+ <string name="lock_to_app_exit" msgid="8967089657201849300">"已退出单应用模式"</string>
+ <string name="lock_to_app_use_screen_lock" msgid="1434584309048590886">"退出前要求输入%1$s"</string>
+ <string name="lock_to_app_unlock_pin" msgid="7908385370846820001">"PIN码"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="7763071104790758405">"解锁图案"</string>
+ <string name="lock_to_app_unlock_password" msgid="795224196583495868">"密码"</string>
</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 43ab412..366ff24 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"允許應用程式接收和處理藍牙 MAP 訊息。這項設定可讓應用程式監控傳送至您裝置的訊息,或在您閱讀訊息前擅自刪除訊息。"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"擷取執行中的應用程式"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"允許應用程式擷取有關目前和最近執行的工作的資訊。如此一來,應用程式或可找出裝置上所使用應用程式的相關資訊。"</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"與其他用戶互動"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"允許應用程式對裝置上的不同用戶執行各種操作。請注意,惡意應用程式可能藉此破壞各用戶之間的保護機制。"</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"全面授權與其他用戶互動"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"允許應用程式修改手機上儲存的瀏覽器記錄或書籤。如此一來,應用程式或可清除或修改瀏覽器資料。注意:這項權限可能不適用於第三方瀏覽器或其他具備網頁瀏覽功能的應用程式。"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"設定鬧鐘"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"允許應用程式在安裝的鬧鐘應用程式中設定鬧鐘,某些鬧鐘應用程式可能沒有這項功能。"</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"寫入留言訊息"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"允許應用程式修改和移除留言信箱中的訊息。"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"新增留言"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"允許應用程式將訊息加到您的留言信箱收件箱。"</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"讀取留言訊息"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"允許應用程式讀取您的留言訊息。"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"修改瀏覽器地理資訊的權限"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"允許應用程式修改瀏覽器的地理資訊權限。惡意應用程式可能會藉此允許將您的位置資訊任意傳送給某些網站。"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"驗證套件"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"允許應用程式繫結至信任的代理程式服務。"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"與更新和復原系統互動"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"允許應用程式與復原系統和系統更新互動。"</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"建立媒體投影工作階段"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"允許應用程式建立媒體投影工作階段。這類工作階段可讓應用程式擷取畫面內容和音訊內容 (一般應用程式並不需用)。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"輕觸兩下即可控制縮放"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"無法新增小工具。"</string>
<string name="ime_action_go" msgid="8320845651737369027">"開始"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"編輯"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"資料用量警告"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"輕觸即可查看使用量和設定。"</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G-3G 數據連線已關閉"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G 數據連線已關閉"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"流動數據連線已關閉"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi 數據連線已關閉"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"已達上限"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"已達 2G-3G 數據上限"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"已達 4G 數據上限"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"已超過流動數據流量上限"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"已達 Wi-Fi 數據上限"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> 超過規定上限。"</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"已限制背景資料"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"系統"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"藍牙音頻"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"無線螢幕分享"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"媒體輸出"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"連接裝置"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"在裝置上放送螢幕"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"正在搜尋裝置…"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 288b314..32d6114 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"允許應用程式接收及處理藍牙 MAP 訊息。這項設定可讓應用程式監控傳送至您裝置的訊息,或在您閱讀訊息前主動刪除訊息。"</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"擷取執行中的應用程式"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"允許應用程式擷取最近執行工作的資訊。這項設定可讓應用程式找出裝置所用程式的相關資訊。"</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"對所有使用者執行各種動作"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"允許應用程式對裝置上的所有使用者執行各種動作。請注意,惡意應用程式可能利用此功能侵害使用者之間的保護機制。"</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"完整授權對所有使用者執行各種動作"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"允許應用程式修改手機上儲存的瀏覽紀錄或書籤。這項設定會讓應用程式具有清除或修改瀏覽資料的權限。注意:這項權限不適用於第三方瀏覽器或其他具備網頁瀏覽功能的應用程式。"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"設定鬧鐘"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"允許應用程式在安裝的鬧鐘應用程式中設定鬧鐘,某些鬧鐘應用程式可能無法執行這項功能。"</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"寫入語音留言"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"允許應用程式修改及移除語音留言收件匣中的訊息。"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"新增語音留言"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"允許應用程式將訊息新增至您的語音信箱收件匣。"</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"讀取語音留言"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"允許應用程式讀取您的語音留言。"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"修改瀏覽器地理資訊的權限"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"允許應用程式修改瀏覽器的地理位置權限。請注意,惡意應用程式可能利用此功能允許將您的位置資訊任意傳送給某些網站。"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"驗證套件"</string>
@@ -1185,11 +1185,11 @@
<string name="whichApplication" msgid="4533185947064773386">"選擇要使用的應用程式"</string>
<string name="whichApplicationNamed" msgid="8260158865936942783">"完成操作需使用 %1$s"</string>
<string name="whichViewApplication" msgid="3272778576700572102">"選擇開啟工具"</string>
- <string name="whichViewApplicationNamed" msgid="2286418824011249620">"使用 %1$s 開啟"</string>
+ <string name="whichViewApplicationNamed" msgid="2286418824011249620">"透過 %1$s 開啟"</string>
<string name="whichEditApplication" msgid="144727838241402655">"選擇編輯工具"</string>
<string name="whichEditApplicationNamed" msgid="1775815530156447790">"使用 %1$s 編輯"</string>
- <string name="whichSendApplication" msgid="6902512414057341668">"選擇分享對象"</string>
- <string name="whichSendApplicationNamed" msgid="2799370240005424391">"與 %1$s 共用"</string>
+ <string name="whichSendApplication" msgid="6902512414057341668">"選擇分享工具"</string>
+ <string name="whichSendApplicationNamed" msgid="2799370240005424391">"透過 %1$s 分享"</string>
<string name="whichHomeApplication" msgid="4616420172727326782">"選取主螢幕應用程式"</string>
<string name="alwaysUse" msgid="4583018368000610438">"設為預設應用程式。"</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"前往 [系統設定] > [應用程式] > [下載] 清除預設值。"</string>
@@ -1390,10 +1390,8 @@
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"允許應用程式繫結至信任的代理程式服務。"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"與更新和還原系統互動"</string>
<string name="permdesc_recovery" msgid="8511774533266359571">"允許應用程式與還原系統及系統更新互動。"</string>
- <!-- no translation found for permlab_createMediaProjection (4941338725487978112) -->
- <skip />
- <!-- no translation found for permdesc_createMediaProjection (1284530992706219702) -->
- <skip />
+ <string name="permlab_createMediaProjection" msgid="4941338725487978112">"建立媒體投影工作階段"</string>
+ <string name="permdesc_createMediaProjection" msgid="1284530992706219702">"允許應用程式建立媒體投影工作階段。這類工作階段可讓應用程式擷取畫面內容和音訊內容 (一般應用程式並不需要)。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"輕觸兩下即可控制縮放"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"無法新增小工具。"</string>
<string name="ime_action_go" msgid="8320845651737369027">"開始"</string>
@@ -1518,20 +1516,14 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"編輯"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"資料用量警告"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"輕觸即可查看使用量和設定。"</string>
- <!-- no translation found for data_usage_3g_limit_title (4462365924791862301) -->
- <skip />
- <!-- no translation found for data_usage_4g_limit_title (7476424187522765328) -->
- <skip />
- <!-- no translation found for data_usage_mobile_limit_title (3393439305227911006) -->
- <skip />
- <!-- no translation found for data_usage_wifi_limit_title (3461968509557554571) -->
- <skip />
- <!-- no translation found for data_usage_limit_body (6131350187562939365) -->
- <skip />
+ <string name="data_usage_3g_limit_title" msgid="4462365924791862301">"2G-3G 數據連線已關閉"</string>
+ <string name="data_usage_4g_limit_title" msgid="7476424187522765328">"4G 數據連線已關閉"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3393439305227911006">"行動數據連線已關閉"</string>
+ <string name="data_usage_wifi_limit_title" msgid="3461968509557554571">"Wi-Fi 數據連線已關閉"</string>
+ <string name="data_usage_limit_body" msgid="6131350187562939365">"已達上限"</string>
<string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"已超過 2G-3G 數據上限"</string>
<string name="data_usage_4g_limit_snoozed_title" msgid="1106562779311209039">"已超過 4G 數據上限"</string>
- <!-- no translation found for data_usage_mobile_limit_snoozed_title (4941346653729943789) -->
- <skip />
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="4941346653729943789">"已超過行動數據流量上限"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"已超過 Wi-Fi 數據上限"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> 超過規定上限。"</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"已限制背景資料"</string>
@@ -1567,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"系統"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"藍牙音訊"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"無線螢幕分享"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"媒體輸出"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"連線至裝置"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"將螢幕投放到裝置上"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"正在搜尋裝置..."</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index d196a21..bd3cc3f 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -298,6 +298,10 @@
<string name="permdesc_receiveBluetoothMap" msgid="8656755936919466345">"Ivumela uhlelo lokusebenza ukuthola nokucubungula imilayezo ye-Bluetooth MAP. Lokhu kusho ukuthi uhlelo lokusebenza lingakwazi ukugada noma ukususa imilayezo ethunyelwa kwidivayisi yakho ngaphandle kokukubonisa yona."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"thola izinhlelo zokusebenza ezisebenzayo"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Ivumela uhlelo lokusebenza ukubuyisa ulwazi mayelana nemisebenzi yamanje neyakamuva. Lokhu kungavumela uhlelo lokusebenza ukuthola ulwazi mayelana nokuthi iziphi izinhlelo zokusebenza ezisetshenziswa kudivayisi."</string>
+ <!-- no translation found for permlab_startTasksFromRecents (8990073877885690623) -->
+ <skip />
+ <!-- no translation found for permdesc_startTasksFromRecents (7382133554871222235) -->
+ <skip />
<string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"ihlanganyela phakathi kwabasebenzisi"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Ivumela uhlelo lokusebenza ukwenza izenzo kubasebenzisi bonke kudivayisi. Izinhlelo zokusebenza ezingalungile zingasebenzisa lokhu ukwephula ukuvikela phakathi kwabasebenzisi."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"ilayisensi egcwele yokuhlanganyela kubasebenzisi"</string>
@@ -1006,16 +1010,12 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Ivumela uhlelo lokusebenza ukushintsha umlando wamabhukhimakhi noma wesiphequluli alondolozwe efonini yakho. Lokhu kungavumela uhlelo lokusebenza ukususa noma ukushintsha idatha yesiphequluli. Qaphela: le mvume kungenzeka ingaphoqelelwa iziphequluli ezivela eceleni noma ezinye izinhlelo zokusebenza ezinamandla okuphequlula iwebhu."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"setha i-alamu"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Ivumela uhlelo lokusebenza ukuthi isethe i-alamu ensizeni efkiwe ye-alamu. Ezinye izinhlelo zokusebenza ze-alamu kungenzeka zingakusebenzisi lokho."</string>
- <!-- no translation found for permlab_writeVoicemail (7309899891683938100) -->
- <skip />
- <!-- no translation found for permdesc_writeVoicemail (6592572839715924830) -->
- <skip />
+ <string name="permlab_writeVoicemail" msgid="7309899891683938100">"bhala ivoyisimeyili"</string>
+ <string name="permdesc_writeVoicemail" msgid="6592572839715924830">"Ivumela uhlelo lokusebenza ukuthi liguqule liphinde lisuse imilayezo esuka kubhokisi lakho lokungenayo lwe-voyisimeyili."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"engeza imeyili yezwi"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Ivumela uhlelo lokusebenza ukwengeza imiyalezo kwibhokisi lakho lemeyili yezwi."</string>
- <!-- no translation found for permlab_readVoicemail (8415201752589140137) -->
- <skip />
- <!-- no translation found for permdesc_readVoicemail (8926534735321616550) -->
- <skip />
+ <string name="permlab_readVoicemail" msgid="8415201752589140137">"funda ivoyisimeyili"</string>
+ <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Ivumela uhlelo lokusebenza ukufunda amavoyisimeyili wakho."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Gugula izimvume zendawo Yesiphequluli"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Ivumela uhlelo lokusebenza ukuthi iguqule izimvume eziphathelene nezindawo Zesiphequluli. Izuhlelo lokusebenza eziyingozi zingasebenzisa lokhu ukuvumela ukuvumela imininingwane yendawo kunoma imaphi amawebusayithi."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"qinisekisa amaphakheji"</string>
@@ -1559,7 +1559,8 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Isistimu"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Umsindo we-Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Ukubonisa okungenazintambo"</string>
- <string name="media_route_button_content_description" msgid="5758553567065145276">"Okukhiphayo kwemidiya"</string>
+ <!-- no translation found for media_route_button_content_description (591703006349356016) -->
+ <skip />
<string name="media_route_chooser_title" msgid="1751618554539087622">"Xhuma kudivayisi"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Lingisa isikrini kudivayisi"</string>
<string name="media_route_chooser_searching" msgid="4776236202610828706">"Isesha amadivayisi…"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f57d61d..0e87893 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3114,6 +3114,28 @@
<attr name="checked" />
<!-- Drawable used for the check mark graphic. -->
<attr name="checkMark" format="reference"/>
+ <!-- Tint to apply to the check mark. -->
+ <attr name="checkMarkTint" format="color" />
+ <!-- Blending mode used to apply the check mark tint. -->
+ <attr name="checkMarkTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
</declare-styleable>
<declare-styleable name="EditText">
</declare-styleable>
@@ -3579,7 +3601,7 @@
result to valid color values. Saturate(S + D) -->
<enum name="add" value="16" />
</attr>
- <!-- Tint to apply to the indepterminate progress indicator. -->
+ <!-- Tint to apply to the indeterminate progress indicator. -->
<attr name="indeterminateTint" format="color" />
<!-- Blending mode used to apply the indeterminate progress indicator tint. -->
<attr name="indeterminateTintMode">
@@ -3733,6 +3755,8 @@
<attr name="shadowRadius" format="float" />
<!-- Elegant text height, especially for less compacted complex script text. -->
<attr name="elegantTextHeight" format="boolean" />
+ <!-- @hide Text letter-spacing. -->
+ <attr name="letterSpacing" format="float" />
</declare-styleable>
<declare-styleable name="TextClock">
<!-- Specifies the formatting pattern used to show the time and/or date
@@ -4026,6 +4050,8 @@
<attr name="textAllCaps" />
<!-- Elegant text height, especially for less compacted complex script text. -->
<attr name="elegantTextHeight" />
+ <!-- @hide Text letter-spacing. -->
+ <attr name="letterSpacing" />
</declare-styleable>
<declare-styleable name="TextViewAppearance">
<!-- Base text color, typeface, size, and style. -->
@@ -4163,6 +4189,8 @@
<attr name="dropDownSelector" />
<!-- Background drawable to use for the dropdown in spinnerMode="dropdown". -->
<attr name="popupBackground" />
+ <!-- Window elevation to use for the dropdown in spinnerMode="dropdown". -->
+ <attr name="popupElevation" />
<!-- Vertical offset from the spinner widget for positioning the dropdown in
spinnerMode="dropdown". -->
<attr name="dropDownVerticalOffset" />
@@ -4887,9 +4915,9 @@
<enum name="disabled" value="-1" />
<!-- Replicates the edge color. -->
<enum name="clamp" value="0" />
- <!-- Repeats the bitmap in both direction. -->
+ <!-- Repeats the bitmap horizontally. -->
<enum name="repeat" value="1" />
- <!-- Repeats the shader's image horizontally and vertically, alternating
+ <!-- Repeats the shader's image horizontally, alternating
mirror images so that adjacent images always seam. -->
<enum name="mirror" value="2" />
</attr>
@@ -4900,9 +4928,9 @@
<enum name="disabled" value="-1" />
<!-- Replicates the edge color. -->
<enum name="clamp" value="0" />
- <!-- Repeats the bitmap in both direction. -->
+ <!-- Repeats the bitmap vertically. -->
<enum name="repeat" value="1" />
- <!-- Repeats the shader's image horizontally and vertically, alternating
+ <!-- Repeats the shader's image vertically, alternating
mirror images so that adjacent images always seam. -->
<enum name="mirror" value="2" />
</attr>
@@ -5446,11 +5474,9 @@
Corresponds to {@link android.transition.Visibility#setMode(int)}. -->
<attr name="visibilityMode">
<!-- Only appearing Views will be supported. -->
- <enum name="mode_in" value="1" />
+ <flag name="mode_in" value="1" />
<!-- Only disappearing Views will be supported. -->
- <enum name="mode_out" value="2" />
- <!-- Both appearing and disappearing views will be supported. -->
- <enum name="mode_in_out" value="3" />
+ <flag name="mode_out" value="2" />
</attr>
</declare-styleable>
<!-- Use <code>target</code> as the root tag of the XML resource that
@@ -6785,16 +6811,18 @@
<!-- Use <code>voice-enrollment-application</code>
as the root tag of the XML resource that escribes the supported keyphrases (hotwords)
by the enrollment application.
- Described here are the attributes that can be included in that tag. -->
+ Described here are the attributes that can be included in that tag.
+ @hide
+ @SystemApi -->
<declare-styleable name="VoiceEnrollmentApplication">
- <!-- A globally unique ID for the keyphrase. -->
+ <!-- A globally unique ID for the keyphrase. @hide @SystemApi -->
<attr name="searchKeyphraseId" format="integer" />
- <!-- The actual keyphrase/hint text, or empty if not keyphrase dependent. -->
+ <!-- The actual keyphrase/hint text, or empty if not keyphrase dependent. @hide @SystemApi -->
<attr name="searchKeyphrase" format="string" />
<!-- A comma separated list of java locales that are supported for this keyphrase,
- or empty if not locale dependent. -->
+ or empty if not locale dependent. @hide @SystemApi -->
<attr name="searchKeyphraseSupportedLocales" format="string" />
- <!-- Flags for supported recognition modes. -->
+ <!-- Flags for supported recognition modes. @hide @SystemApi -->
<attr name="searchKeyphraseRecognitionFlags">
<flag name="none" value="0" />
<flag name="voiceTrigger" value="0x1" />
@@ -6876,6 +6904,9 @@
<attr name="contentInsetRight" format="dimension" />
<!-- Elevation for the action bar itself -->
<attr name="elevation" />
+ <!-- Reference to a theme that should be used to inflate popups
+ shown by widgets in the action bar. -->
+ <attr name="popupTheme" />
</declare-styleable>
<declare-styleable name="ActionMode">
@@ -7164,6 +7195,9 @@
<flag name="bottom" value="0x50" />
</attr>
<attr name="collapseIcon" format="reference" />
+ <!-- Reference to a theme that should be used to inflate popups
+ shown by widgets in the toolbar. -->
+ <attr name="popupTheme" format="reference" />
</declare-styleable>
<declare-styleable name="Toolbar_LayoutParams">
@@ -7191,19 +7225,6 @@
the settings for this service. -->
<attr name="settingsActivity" />
<!-- Type of this service. -->
- <attr name="tvInputType">
- <!-- Should be in sync with constant values defined in
- {@link android.media.tv.TvInputInfo}. -->
-
- <!-- Virtual input (default) -->
- <enum name="virtual" value="0" />
- <!-- HDMI -->
- <enum name="hdmi" value="1" />
- <!-- Built-in tuner -->
- <enum name="tuner" value="2" />
- <!-- Pass-through -->
- <enum name="passthrough" value="3" />
- </attr>
</declare-styleable>
<declare-styleable name="ResolverDrawerLayout">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 021edc5..c268d97 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -212,6 +212,9 @@
<!-- Additional flag from base permission type: this permission can also
(optionally) be granted to development applications. -->
<flag name="development" value="0x20" />
+ <!-- Additional flag from base permission type: this permission is closely
+ associated with an app op for controlling access. -->
+ <flag name="appop" value="0x40" />
</attr>
<!-- Flags indicating more context for a permission group. -->
@@ -905,7 +908,7 @@
<enum name="persistRootOnly" value="0" />
<!-- If this activity forms the root of a task then that task will not be persisted
across reboots -->
- <enum name="doNotPersist" value="1" />
+ <enum name="persistNever" value="1" />
<!-- If this activity forms the root of a task then the task and this activity will
be persisted across reboots. If the activity above this activity is also
tagged with the attribute <code>"persist"</code> then it will be persisted as well.
@@ -1282,6 +1285,19 @@
<attr name="required" format="boolean" />
</declare-styleable>
+ <!-- The <code>feature-group</code> tag specifies
+ a set of one or more <code>uses-feature</code> elements that
+ the application can utilize. An application uses multiple
+ <code>feature-group</code> sets to indicate that it can support
+ different combinations of features.
+
+ <p>This appears as a child tag of the root
+ {@link #AndroidManifest manifest} tag. -->
+ <declare-styleable name="AndroidManifestFeatureGroup">
+ <!-- The human-readable name of the feature group. -->
+ <attr name="label" />
+ </declare-styleable>
+
<!-- The <code>uses-sdk</code> tag describes the SDK features that the
containing package must be running on to operate correctly.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0954ddf..7f160e8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1588,6 +1588,9 @@
<!-- default device has recents property -->
<bool name="config_hasRecents">true</bool>
+ <!-- default window ShowCircularMask property -->
+ <bool name="config_windowShowCircularMask">false</bool>
+
<!-- Defines the default set of global actions. Actions may still be disabled or hidden based
on the current state of the device.
Each item must be one of the following strings:
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 77b115b..afe180f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -384,4 +384,9 @@
<dimen name="immersive_mode_cling_width">-1px</dimen>
<dimen name="resolver_max_width">480dp</dimen>
+
+ <!-- Size of the offset applied to the position of the circular mask. This is only
+ used on circular displays. In the case where there is no "chin", this will default
+ to 0 -->
+ <dimen name="circular_display_mask_offset">0px</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e4484ad..b1902dd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2097,7 +2097,6 @@
<public type="attr" name="isGame" id="0x10103f4" />
<public type="attr" name="allowEmbedded" id="0x10103f5" />
<public type="attr" name="setupActivity" id="0x10103f6"/>
- <public type="attr" name="tvInputType" id="0x10103f7"/>
<!-- ===============================================================
Resources added in version 21 of the platform
@@ -2240,7 +2239,7 @@
<public type="attr" name="launchTaskBehindSourceAnimation" />
<!-- Attribute specified in a restriction entry to denote the type of restriction. -->
<public type="attr" name="restrictionType" />
-
+ <!-- For the DatePicker -->
<public type="attr" name="dateSelectorDayOfWeekBackgroundColor" />
<public type="attr" name="dateSelectorDayOfWeekTextAppearance" />
<public type="attr" name="dateSelectorBackgroundColor" />
@@ -2251,7 +2250,6 @@
<public type="attr" name="dateSelectorYearListSelectedCircleColor" />
<public type="attr" name="calendarTextColor" />
<public type="attr" name="recognitionService" />
-
<!-- For the TimePicker -->
<public type="attr" name="timePickerStyle" />
<public type="attr" name="timePickerDialogTheme" />
@@ -2265,8 +2263,12 @@
<public type="attr" name="amPmTextColor" />
<public type="attr" name="amPmUnselectedBackgroundColor" />
<public type="attr" name="amPmSelectedBackgroundColor" />
-
<public type="attr" name="searchKeyphraseRecognitionFlags" />
+ <public type="attr" name="checkMarkTint" />
+ <public type="attr" name="checkMarkTintMode" />
+ <public type="attr" name="popupTheme" />
+ <public type="attr" name="toolbarStyle" />
+ <public type="attr" name="letterSpacing" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0d9da35..351acf0 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -774,6 +774,12 @@
about currently and recently running tasks. This may allow the app to
discover information about which applications are used on the device.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_startTasksFromRecents">start a task from recents</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_startTasksFromRecents">Allows the app to use an ActivityManager.RecentTaskInfo
+ object to launch a defunct task that was returned from ActivityManager.getRecentTaskList().</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
<string name="permlab_interactAcrossUsers">interact across users</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
@@ -3920,6 +3926,11 @@
<string name="permission_request_notification_title">Permission requested</string>
<string name="permission_request_notification_with_subtitle">Permission requested\nfor account <xliff:g id="account" example="foo@gmail.com">%s</xliff:g>.</string>
+ <!-- Message to show when an intent automatically switches users into the personal profile. -->
+ <string name="forward_intent_to_owner">You\'re using this app in your personal space</string>
+ <!-- Message to show when an intent automatically switches users into a work profile. -->
+ <string name="forward_intent_to_work">You\'re using this app in your work space</string>
+
<!-- Label to show for a service that is running because it is an input method. -->
<string name="input_method_binding_label">Input method</string>
<!-- Label to show for a service that is running because it is a sync adapter. -->
@@ -4304,8 +4315,9 @@
<!-- Description of a wireless display route. [CHAR LIMIT=50] -->
<string name="wireless_display_route_description">Wireless display</string>
- <!-- Content description of a MediaRouteButton for accessibility support -->
- <string name="media_route_button_content_description">Media output</string>
+ <!-- Content description of a MediaRouteButton for accessibility support.
+ Cast is the standard android verb for sending content to a remote device. [CHAR LIMIT=50] -->
+ <string name="media_route_button_content_description">Cast</string>
<!-- Title of the media route chooser dialog. [CHAR LIMIT=40] -->
<string name="media_route_chooser_title">Connect to device</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index b9cacd7..2dccaf2 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1203,6 +1203,7 @@
<item name="navigationButtonStyle">@style/Widget.Toolbar.Button.Navigation</item>
<item name="collapseIcon">?attr/homeAsUpIndicator</item>
<item name="contentInsetStart">16dp</item>
+ <item name="touchscreenBlocksFocus">true</item>
</style>
<style name="Widget.Toolbar.Button.Navigation" parent="Widget">
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 575bbe83..a8974c6 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -543,6 +543,7 @@
<style name="Widget.Material.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
<item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_material</item>
+ <item name="popupElevation">@dimen/floating_window_z</item>
</style>
<style name="Widget.Material.CompoundButton" parent="Widget.CompoundButton"/>
@@ -663,6 +664,7 @@
<style name="Widget.Material.PopupWindow.ActionMode">
<item name="popupBackground">@drawable/popup_background_material</item>
+ <item name="popupElevation">@dimen/floating_window_z</item>
<item name="popupAnimationStyle">@style/Animation.PopupWindow.ActionMode</item>
</style>
@@ -731,6 +733,7 @@
<item name="background">@drawable/spinner_background_material</item>
<item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_material</item>
+ <item name="popupElevation">@dimen/floating_window_z</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
<item name="dropDownWidth">wrap_content</item>
@@ -863,14 +866,15 @@
<item name="divider">?attr/dividerVertical</item>
<item name="titleTextStyle">@style/TextAppearance.Material.Widget.ActionBar.Title</item>
<item name="subtitleTextStyle">@style/TextAppearance.Material.Widget.ActionBar.Subtitle</item>
- <item name="progressBarStyle">@style/Widget.Material.ProgressBar.Horizontal</item>
- <item name="indeterminateProgressStyle">@style/Widget.Material.ProgressBar</item>
+ <item name="progressBarStyle">?attr/progressBarStyleHorizontal</item>
+ <item name="indeterminateProgressStyle">?attr/progressBarStyle</item>
<item name="progressBarPadding">32dip</item>
<item name="itemPadding">8dip</item>
<item name="homeLayout">@layout/action_bar_home_material</item>
<item name="gravity">center_vertical</item>
<item name="contentInsetStart">16dp</item>
<item name="elevation">8dp</item>
+ <item name="popupTheme">?attr/actionBarPopupTheme</item>
</style>
<style name="Widget.Material.ActionBar.Solid">
@@ -1032,23 +1036,8 @@
<style name="Widget.Material.Light.ActionBar.TabText" parent="Widget.Material.ActionBar.TabText"/>
<style name="Widget.Material.Light.ActionMode" parent="Widget.Material.ActionMode" />
<style name="Widget.Material.Light.ActionButton.CloseMode" parent="Widget.Material.ActionButton.CloseMode" />
-
- <style name="Widget.Material.Light.ActionBar" parent="Widget.Material.ActionBar">
- <item name="titleTextStyle">@style/TextAppearance.Material.Widget.ActionBar.Title</item>
- <item name="subtitleTextStyle">@style/TextAppearance.Material.Widget.ActionBar.Subtitle</item>
- <item name="background">@null</item>
- <item name="backgroundStacked">@null</item>
- <item name="backgroundSplit">@null</item>
- <item name="progressBarStyle">@style/Widget.Material.Light.ProgressBar.Horizontal</item>
- <item name="indeterminateProgressStyle">@style/Widget.Material.Light.ProgressBar</item>
- </style>
-
- <style name="Widget.Material.Light.ActionBar.Solid">
- <item name="background">?attr/colorPrimary</item>
- <item name="backgroundStacked">?attr/colorPrimary</item>
- <item name="backgroundSplit">?attr/colorPrimary</item>
- </style>
-
+ <style name="Widget.Material.Light.ActionBar" parent="Widget.Material.ActionBar" />
+ <style name="Widget.Material.Light.ActionBar.Solid" parent="Widget.Material.ActionBar.Solid" />
<style name="Widget.Material.Light.FastScroll" parent="Widget.Material.FastScroll"/>
<style name="Widget.Material.Light.MediaRouteButton" parent="Widget.Material.MediaRouteButton" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c324e93..e9b9889 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -16,7 +16,6 @@
*/
-->
<resources>
-
<!-- We don't want to publish private symbols in android.R as part of the
SDK. Instead, put them here. -->
<private-symbols package="com.android.internal" />
@@ -290,6 +289,7 @@
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
<java-symbol type="bool" name="config_windowIsRound" />
<java-symbol type="bool" name="config_hasRecents" />
+ <java-symbol type="bool" name="config_windowShowCircularMask" />
<java-symbol type="integer" name="config_bluetooth_max_advertisers" />
<java-symbol type="integer" name="config_bluetooth_max_scan_filters" />
@@ -350,6 +350,7 @@
<java-symbol type="dimen" name="notification_title_text_size" />
<java-symbol type="dimen" name="notification_subtext_size" />
<java-symbol type="dimen" name="immersive_mode_cling_width" />
+ <java-symbol type="dimen" name="circular_display_mask_offset" />
<java-symbol type="string" name="add_account_button_label" />
<java-symbol type="string" name="addToDictionary" />
@@ -1432,6 +1433,8 @@
<java-symbol type="style" name="Animation.LockScreen" />
<java-symbol type="style" name="Theme.Dialog.RecentApplications" />
<java-symbol type="style" name="Theme.ExpandedMenu" />
+ <java-symbol type="string" name="forward_intent_to_owner" />
+ <java-symbol type="string" name="forward_intent_to_work" />
<!-- From services -->
<java-symbol type="anim" name="screen_rotate_0_enter" />
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java
new file mode 100644
index 0000000..5e451ca
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.le;
+
+import android.os.Parcel;
+import android.os.ParcelUuid;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test cases for {@link AdvertiseData}.
+ * <p>
+ * To run the test, use adb shell am instrument -e class 'android.bluetooth.le.AdvertiseDataTest' -w
+ * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
+ */
+public class AdvertiseDataTest extends TestCase {
+
+ private AdvertiseData.Builder mAdvertiseDataBuilder;
+
+ @Override
+ protected void setUp() throws Exception {
+ mAdvertiseDataBuilder = new AdvertiseData.Builder();
+ }
+
+ @SmallTest
+ public void testEmptyData() {
+ Parcel parcel = Parcel.obtain();
+ AdvertiseData data = mAdvertiseDataBuilder.build();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AdvertiseData dataFromParcel =
+ AdvertiseData.CREATOR.createFromParcel(parcel);
+ assertEquals(data, dataFromParcel);
+ }
+
+ @SmallTest
+ public void testEmptyServiceUuid() {
+ Parcel parcel = Parcel.obtain();
+ AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true).build();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AdvertiseData dataFromParcel =
+ AdvertiseData.CREATOR.createFromParcel(parcel);
+ assertEquals(data, dataFromParcel);
+ }
+
+ @SmallTest
+ public void testEmptyManufacturerData() {
+ Parcel parcel = Parcel.obtain();
+ int manufacturerId = 50;
+ byte[] manufacturerData = new byte[0];
+ AdvertiseData data =
+ mAdvertiseDataBuilder.setIncludeDeviceName(true)
+ .setManufacturerData(manufacturerId, manufacturerData).build();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AdvertiseData dataFromParcel =
+ AdvertiseData.CREATOR.createFromParcel(parcel);
+ assertEquals(data, dataFromParcel);
+ }
+
+ @SmallTest
+ public void testEmptyServiceData() {
+ Parcel parcel = Parcel.obtain();
+ ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
+ byte[] serviceData = new byte[0];
+ AdvertiseData data =
+ mAdvertiseDataBuilder.setIncludeDeviceName(true)
+ .setServiceData(uuid, serviceData).build();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AdvertiseData dataFromParcel =
+ AdvertiseData.CREATOR.createFromParcel(parcel);
+ assertEquals(data, dataFromParcel);
+ }
+
+ @SmallTest
+ public void testServiceUuid() {
+ Parcel parcel = Parcel.obtain();
+ ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
+ ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
+
+ AdvertiseData data =
+ mAdvertiseDataBuilder.setIncludeDeviceName(true)
+ .addServiceUuid(uuid).addServiceUuid(uuid2).build();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AdvertiseData dataFromParcel =
+ AdvertiseData.CREATOR.createFromParcel(parcel);
+ assertEquals(data, dataFromParcel);
+ }
+
+ @SmallTest
+ public void testManufacturerData() {
+ Parcel parcel = Parcel.obtain();
+ ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
+ ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
+
+ int manufacturerId = 50;
+ byte[] manufacturerData = new byte[] {
+ (byte) 0xF0, 0x00, 0x02, 0x15 };
+ AdvertiseData data =
+ mAdvertiseDataBuilder.setIncludeDeviceName(true)
+ .addServiceUuid(uuid).addServiceUuid(uuid2)
+ .setManufacturerData(manufacturerId, manufacturerData).build();
+
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AdvertiseData dataFromParcel =
+ AdvertiseData.CREATOR.createFromParcel(parcel);
+ assertEquals(data, dataFromParcel);
+ }
+
+ @SmallTest
+ public void testServiceData() {
+ Parcel parcel = Parcel.obtain();
+ ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
+ byte[] serviceData = new byte[] {
+ (byte) 0xF0, 0x00, 0x02, 0x15 };
+ AdvertiseData data =
+ mAdvertiseDataBuilder.setIncludeDeviceName(true)
+ .setServiceData(uuid, serviceData).build();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AdvertiseData dataFromParcel =
+ AdvertiseData.CREATOR.createFromParcel(parcel);
+ assertEquals(data, dataFromParcel);
+ }
+}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
index 25ea227..81f4baf 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
@@ -186,7 +186,6 @@
parcel.setDataPosition(0);
ScanFilter filterFromParcel =
ScanFilter.CREATOR.createFromParcel(parcel);
- System.out.println(filterFromParcel);
assertEquals(filter, filterFromParcel);
}
}
diff --git a/core/tests/coretests/src/android/net/NetworkKeyTest.java b/core/tests/coretests/src/android/net/NetworkKeyTest.java
deleted file mode 100644
index 9005188..0000000
--- a/core/tests/coretests/src/android/net/NetworkKeyTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.net;
-
-import android.os.Parcel;
-
-import junit.framework.TestCase;
-
-public class NetworkKeyTest extends TestCase {
- public void testValidWifiKey_utf8() {
- new WifiKey("\"quotedSsid\"", "AB:CD:01:EF:23:03");
- new WifiKey("\"\"", "AB:CD:01:EF:23:03");
- }
-
- public void testValidWifiKey_hex() {
- new WifiKey("0x1234abcd", "AB:CD:01:EF:23:03");
- }
-
- public void testInvalidWifiKey_empty() {
- try {
- new WifiKey("", "AB:CD:01:EF:23:03");
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected - empty SSID
- }
- }
-
- public void testInvalidWifiKey_unquotedUtf8() {
- try {
- new WifiKey("unquotedSsid", "AB:CD:01:EF:23:03");
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected - empty SSID
- }
- }
-
- public void testInvalidWifiKey_invalidHex() {
- try {
- new WifiKey("0x\"nothex\"", "AB:CD:01:EF:23:03");
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected - empty SSID
- }
- }
-
- public void testInvalidWifiKey_shortBssid() {
- try {
- new WifiKey("\"quotedSsid\"", "AB:CD:01:EF:23");
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected - BSSID too short
- }
- }
-
- public void testInvalidWifiKey_longBssid() {
- try {
- new WifiKey("\"quotedSsid\"", "AB:CD:01:EF:23:03:11");
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected - BSSID too long
- }
- }
-
- public void testParceling() {
- WifiKey wifiKey = new WifiKey("\"ssid\"", "00:00:00:00:00:00");
- NetworkKey networkKey = new NetworkKey(wifiKey);
- Parcel parcel = null;
- try {
- parcel = Parcel.obtain();
- parcel.writeParcelable(networkKey, 0);
- parcel.setDataPosition(0);
- networkKey = parcel.readParcelable(getClass().getClassLoader());
- } finally {
- if (parcel != null) {
- parcel.recycle();
- }
- }
-
- assertEquals(NetworkKey.TYPE_WIFI, networkKey.type);
- assertEquals("\"ssid\"", networkKey.wifiKey.ssid);
- assertEquals("00:00:00:00:00:00", networkKey.wifiKey.bssid);
- }
-}
diff --git a/core/tests/coretests/src/android/net/RssiCurveTest.java b/core/tests/coretests/src/android/net/RssiCurveTest.java
deleted file mode 100644
index d4438df..0000000
--- a/core/tests/coretests/src/android/net/RssiCurveTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.net;
-
-import junit.framework.TestCase;
-
-public class RssiCurveTest extends TestCase {
- public void testLookupScore_constantCurve() {
- RssiCurve curve = new RssiCurve(-100, 200, new byte[] { 10 });
- assertEquals(10, curve.lookupScore(-200));
- assertEquals(10, curve.lookupScore(-100));
- assertEquals(10, curve.lookupScore(0));
- assertEquals(10, curve.lookupScore(100));
- assertEquals(10, curve.lookupScore(200));
- }
-
- public void testLookupScore_changingCurve() {
- RssiCurve curve = new RssiCurve(-100, 100, new byte[] { -10, 10 });
- assertEquals(-10, curve.lookupScore(-200));
- assertEquals(-10, curve.lookupScore(-100));
- assertEquals(-10, curve.lookupScore(-50));
- assertEquals(10, curve.lookupScore(0));
- assertEquals(10, curve.lookupScore(50));
- assertEquals(10, curve.lookupScore(100));
- assertEquals(10, curve.lookupScore(200));
- }
-}
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
deleted file mode 100644
index 7ab69ad..0000000
--- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.net;
-
-import android.os.Parcel;
-
-import junit.framework.TestCase;
-
-import java.util.Arrays;
-
-public class ScoredNetworkTest extends TestCase {
- private static final RssiCurve CURVE =
- new RssiCurve(-110, 10, new byte[] {0, 1, 2, 3, 4, 5, 6, 7});
-
- public void testInvalidCurve_nullBuckets() {
- try {
- new RssiCurve(-110, 10, null);
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected
- }
- }
-
- public void testInvalidCurve_emptyBuckets() {
- try {
- new RssiCurve(-110, 10, new byte[] {});
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected
- }
- }
-
- public void testParceling() {
- NetworkKey key = new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00"));
- ScoredNetwork network = new ScoredNetwork(key, CURVE);
- Parcel parcel = null;
- try {
- parcel = Parcel.obtain();
- parcel.writeParcelable(network, 0);
- parcel.setDataPosition(0);
- network = parcel.readParcelable(getClass().getClassLoader());
- } finally {
- if (parcel != null) {
- parcel.recycle();
- }
- }
- assertEquals(CURVE.start, network.rssiCurve.start);
- assertEquals(CURVE.bucketWidth, network.rssiCurve.bucketWidth);
- assertTrue(Arrays.equals(CURVE.rssiBuckets, network.rssiCurve.rssiBuckets));
- }
-}
diff --git a/docs/html/about/versions/android-4.2.jd b/docs/html/about/versions/android-4.2.jd
index 73d51c5..76acb8a 100644
--- a/docs/html/about/versions/android-4.2.jd
+++ b/docs/html/about/versions/android-4.2.jd
@@ -361,8 +361,7 @@
href="{@docRoot}google/gcm/index.html">Google Cloud Messaging</a>.</p>
<p>Beware that if your app requests one of the hardware device identifiers (such as the WiFi MAC
-address, the {@link android.os.Build#SERIAL} number, or the {@link
-android.provider.Settings.Secure#ANDROID_ID} number), they will provide the same value for each
+address or the {@link android.os.Build#SERIAL} number), they will provide the same value for each
user because these identifiers are tied to the hardware and not the user. Not to mention the other
problems these identifiers introduce as discussed in the <a
href="http://android-developers.blogspot.com/2011/03/identifying-app-installations.html">Identifying
diff --git a/docs/html/design/downloads/index.jd b/docs/html/design/downloads/index.jd
index eec4e8d..1664923 100644
--- a/docs/html/design/downloads/index.jd
+++ b/docs/html/design/downloads/index.jd
@@ -90,10 +90,10 @@
</div>
<div class="layout-content-col span-4">
<a class="download-button" onClick="_gaq.push(['_trackEvent', 'Design', 'Download', 'Wear Toolkit AI']);"
- href="{@docRoot}downloads/design/Android_Wear_Toolkit_20140626.ai">Adobe® Illustrator® Toolkit</a>
+ href="{@docRoot}downloads/design/Android_Wear_Toolkit_20140722.ai">Adobe® Illustrator® Toolkit</a>
<a class="download-button" onClick="_gaq.push(['_trackEvent', 'Design', 'Download', 'Wear Toolkit PDF']);"
- href="{@docRoot}downloads/design/Android_Wear_Toolkit_20140626.pdf">PDF Toolkit</a>
+ href="{@docRoot}downloads/design/Android_Wear_Toolkit_20140722.pdf">PDF Toolkit</a>
</div>
</div>
@@ -111,9 +111,9 @@
</div>
<div class="layout-content-col span-4">
<a class="download-button" onClick="_gaq.push(['_trackEvent', 'Design', 'Download', 'Wear App Patterns AI']);"
- href="{@docRoot}downloads/design/Android_Wear_Patterns_20140626.ai">Adobe® Illustrator® App Patterns</a>
+ href="{@docRoot}downloads/design/Android_Wear_Patterns_20140722.ai">Adobe® Illustrator® App Patterns</a>
<a class="download-button" onClick="_gaq.push(['_trackEvent', 'Design', 'Download', 'Wear App Patterns PDF']);"
- href="{@docRoot}downloads/design/Android_Wear_Patterns_20140626.pdf">PDF App Patterns</a>
+ href="{@docRoot}downloads/design/Android_Wear_Patterns_20140722.pdf">PDF App Patterns</a>
</div>
</div>
diff --git a/docs/html/google/play-services/ads.jd b/docs/html/google/play-services/ads.jd
index ed346c6..52f643e 100644
--- a/docs/html/google/play-services/ads.jd
+++ b/docs/html/google/play-services/ads.jd
@@ -59,6 +59,11 @@
<h4>Flexible and powerful tools </h4>
<p>Filters and controls help you manage your ads. If you want to use multiple ad
networks, you can do that too, with free ad network mediation.</p>
+
+ <h4>Ad Policy Compliant</h4>
+ <p>The Google Mobile Ads SDK available in Google Play services is compliant with
+ Google Play Ad Poilcy regarding the usage of
+ <a href="{@docRoot}google/play-services/id.html">advertising ID</a>.</p>
</div>
<div class="col-6 normal-links">
diff --git a/docs/html/google/play-services/id.jd b/docs/html/google/play-services/id.jd
index db50c7f..43d1809 100644
--- a/docs/html/google/play-services/id.jd
+++ b/docs/html/google/play-services/id.jd
@@ -55,6 +55,19 @@
</div>
</div>
+<p class="caution" style=
+"background-color:#fffdeb;width:100%;margin-bottom:1em;padding:.5em;">
+ As a reminder, please note that starting <strong>1 August 2014</strong>, new
+ apps and app updates distributed through Google Play must use the advertising
+ ID in lieu of any other persistent identifiers for any advertising purposes,
+ on devices that support the advertising ID.<br>
+ <br>
+ To learn how to check your app's compliance through the Developer Console, or
+ for details on the associated developer policy changes, please see the
+ <a href=
+ "https://support.google.com/googleplay/android-developer/answer/6048248">Advertising
+ ID topic</a> in the Google Play developer help center.
+</p>
<h2 id="get_started">Using the Advertising ID</h2>
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index c4d5083..f0e93b9 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -453,7 +453,7 @@
created to handle a new intent. However, if the target task already has an
existing instance of the activity at the top of its stack, that instance
will receive the new intent (in an
-<code>{@link android.app.Activity#onNewIntent onNewIntent()}</code> call);
+{@link android.app.Activity#onNewIntent onNewIntent()} call);
a new instance is not created.
In other circumstances — for example, if an existing instance of the
"{@code singleTop}" activity is in the target task, but not at the top of
@@ -461,6 +461,22 @@
— a new instance would be created and pushed on the stack.
</p>
+<p>Similarly, if you
+<a href="{@docRoot}training/implementing-navigation/ancestral.html">navigate
+up</a> to an activity on the current stack, the behavior is determined by the
+parent activity's launch mode. If the parent activity has launch mode {@code
+singleTop} (or the <code>up</code> intent contains {@link
+android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}), the parent is brought to the
+top of the stack, and its state is preserved. The navigation intent is received
+by the parent activity's {@link android.app.Activity#onNewIntent onNewIntent()}
+method. If the parent activity has launch mode {@code standard} (and the
+<code>up</code> intent does not contain {@link
+android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}), the current activity and its
+parent are both popped off the stack, and a new instance of the parent activity
+is created to receive the navigation intent.
+</p>
+
+
<p>
The "{@code singleTask}" and "{@code singleInstance}" modes also differ from
each other in only one respect: A "{@code singleTask}" activity allows other
diff --git a/docs/html/guide/topics/resources/localization.jd b/docs/html/guide/topics/resources/localization.jd
index e86d4c9..1ee6606 100644
--- a/docs/html/guide/topics/resources/localization.jd
+++ b/docs/html/guide/topics/resources/localization.jd
@@ -402,8 +402,7 @@
resolution and density of the device screen may differ, which could affect
the display of strings and drawables in your UI.</p>
-<p>To change the locale on a device, use the Settings application (Home >
-Menu > Settings > Locale & text > Select locale). </p>
+<p>To change the locale or language on a device, use the Settings application.</p>
<h3 id="emulator">Testing on an Emulator</h3>
diff --git a/docs/html/guide/topics/text/creating-input-method.jd b/docs/html/guide/topics/text/creating-input-method.jd
index 205fd96..4b6b7b7 100644
--- a/docs/html/guide/topics/text/creating-input-method.jd
+++ b/docs/html/guide/topics/text/creating-input-method.jd
@@ -4,6 +4,30 @@
<div id="qv-wrapper">
<div id="qv">
+<h2>In This Document</h2>
+<ol>
+ <li>
+ <a href="#InputMethodLifecycle">The IME Lifecycle</a>
+ </li>
+ <li>
+ <a href="#DefiningIME">Declaring IME Components in the Manifest</a>
+ </li>
+ <li>
+ <a href="#IMEAPI">The Input Method API</a>
+ </li>
+ <li>
+ <a href="#IMEUI">Designing the Input Method UI</a>
+ </li>
+ <li>
+ <a href="#SendText">Sending Text to the Application</a>
+ </li>
+ <li>
+ <a href="#IMESubTypes">Creating an IME Subtype</a>
+ </li>
+ <li>
+ <a href="#GeneralDesign">General IME Considerations</a>
+ </li>
+</ol>
<h2>See also</h2>
<ol>
<li>
@@ -16,29 +40,20 @@
</div>
</div>
<p>
- An input method editor (IME) is a user control that enables users to enter text. Android
- provides an extensible input method framework that allows applications to provide users
- alternative input methods, such as on-screen keyboards or even speech input. Once installed,
- users can select which IME they want to use from the system settings and use it across the
+ An input method editor (IME) is a user control that enables users to enter text. Android
+ provides an extensible input method framework that allows applications to provide users
+ alternative input methods, such as on-screen keyboards or even speech input. Once installed,
+ users can select which IME they want to use from the system settings and use it across the
entire system; only one IME may be enabled at a time.
</p>
<p>
To add an IME to the Android system, you create an Android application
- containing a class that extends {@link android.inputmethodservice.InputMethodService}. In
+ containing a class that extends {@link android.inputmethodservice.InputMethodService}. In
addition, you usually create a "settings" activity that passes options to the IME
service. You can also define a settings UI that's displayed as part of the system settings.
</p>
-<p>This article covers the following:</p>
-<ul>
- <li>The IME lifecycle.</li>
- <li>Declaring IME components in the application manifest.</li>
- <li>The IME API.</li>
- <li>Designing an IME UI.</li>
- <li>Sending text from an IME to an application.</li>
- <li>Working with IME subtypes.</li>
-</ul>
<p>
- If you haven't worked with IMEs before, you should read the introductory article
+ If you haven't worked with IMEs before, you should read the introductory article
<a href="http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html">Onscreen Input Methods</a> first.
Also, the Soft Keyboard sample app included in the SDK contains sample code that you can modify
to start building your own IME.
@@ -59,16 +74,16 @@
<h2 id="DefiningIME">Declaring IME Components in the Manifest</h2>
<p>
In the Android system, an IME is an Android application that contains a special IME service.
- The application's manifest file must declare the service, request the necessary permissions,
- provide an intent filter that matches the action <code>action.view.InputMethod</code>, and
+ The application's manifest file must declare the service, request the necessary permissions,
+ provide an intent filter that matches the action <code>action.view.InputMethod</code>, and
provide metadata that defines characteristics of the IME. In addition, to provide a settings
interface that allows the user to modify the behavior of the IME, you can define a "settings"
activity that can be launched from System Settings.
</p>
<p>
The following snippet declares IME service. It requests the permission {@link
- android.Manifest.permission#BIND_INPUT_METHOD} to allow the service to connect the IME to
- the system, sets up an intent filter that matches the action
+ android.Manifest.permission#BIND_INPUT_METHOD} to allow the service to connect the IME to
+ the system, sets up an intent filter that matches the action
<code>android.view.InputMethod</code>, and defines metadata for the IME:
</p>
<pre>
@@ -88,7 +103,7 @@
for the IME application:</p>
<pre>
<!-- Optional: an activity for controlling the IME settings -->
- <activity android:name="FastInputIMESettings"
+ <activity android:name="FastInputIMESettings"
android:label="@string/fast_input_settings">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -105,12 +120,12 @@
handling keyboard characters.
</p>
<p>
- The central part of an IME is a service component, a class that extends
- {@link android.inputmethodservice.InputMethodService}. In addition to implementing the
- normal service lifecycle, this class has callbacks for providing your IME's UI, handling user
+ The central part of an IME is a service component, a class that extends
+ {@link android.inputmethodservice.InputMethodService}. In addition to implementing the
+ normal service lifecycle, this class has callbacks for providing your IME's UI, handling user
input, and delivering text to the field that currently has focus. By default, the
- {@link android.inputmethodservice.InputMethodService} class provides most of the implementation
- for managing the state and visibility of the IME and communicating with the current
+ {@link android.inputmethodservice.InputMethodService} class provides most of the implementation
+ for managing the state and visibility of the IME and communicating with the current
input field.
</p>
<p>
@@ -122,13 +137,13 @@
Defines the communication channel from an {@link android.view.inputmethod.InputMethod}
back to the application that is receiving its input. You use it to read text around the
cursor, commit text to the text box, and send raw key events to the application.
- Applications should extend this class rather than implementing the base interface
+ Applications should extend this class rather than implementing the base interface
{@link android.view.inputmethod.InputConnection}.
</dd>
<dt>{@link android.inputmethodservice.KeyboardView}</dt>
<dd>
An extension of {@link android.view.View} that renders a keyboard and responds to user
- input events. The keyboard layout is specified by an instance of
+ input events. The keyboard layout is specified by an instance of
{@link android.inputmethodservice.Keyboard}, which you can define in an XML file.
</dd>
</dl>
@@ -141,40 +156,40 @@
<h3 id="InputView">Input view</h3>
<p>
The input view is the UI where the user inputs text, in the form of keyclicks, handwriting or
- gestures. When the iIME is displayed for the first time, the system calls the
+ gestures. When the iIME is displayed for the first time, the system calls the
{@link android.inputmethodservice.InputMethodService#onCreateInputView()} callback. In your
implementation of this method, you create the layout you want to display in the IME
window and return the layout to the system. This snippet is an example of implementing the
{@link android.inputmethodservice.InputMethodService#onCreateInputView()} method:
<pre>
- @Override
- public View onCreateInputView() {
- MyKeyboardView inputView =
+ @Override
+ public View onCreateInputView() {
+ MyKeyboardView inputView =
(MyKeyboardView) getLayoutInflater().inflate( R.layout.input, null);
-
- inputView.setOnKeyboardActionListener(this); inputView.setKeyboard(mLatinKeyboard);
-
- return mInputView;
- }
+
+ inputView.setOnKeyboardActionListener(this); inputView.setKeyboard(mLatinKeyboard);
+
+ return mInputView;
+ }
</pre>
<p>
- In this example, {@code MyKeyboardView} is an instance of a custom implementation of
- {@link android.inputmethodservice.KeyboardView} that renders a
- {@link android.inputmethodservice.Keyboard}. If you’re building a traditional QWERTY keyboard,
- see the Soft Keyboard <a href="{@docRoot}tools/samples/index.html">sample
+ In this example, {@code MyKeyboardView} is an instance of a custom implementation of
+ {@link android.inputmethodservice.KeyboardView} that renders a
+ {@link android.inputmethodservice.Keyboard}. If you’re building a traditional QWERTY keyboard,
+ see the Soft Keyboard <a href="{@docRoot}tools/samples/index.html">sample
app</a> for an example of how to extend the {@link android.inputmethodservice.KeyboardView} class.
</p>
<h3 id="CandidateView">Candidates view</h3>
<p>
The candidates view is the UI where the IME displays potential word corrections or
- suggestions for the user to select. In the IME lifecycle, the system calls
- {@link android.inputmethodservice.InputMethodService#onCreateCandidatesView()} when it's ready
+ suggestions for the user to select. In the IME lifecycle, the system calls
+ {@link android.inputmethodservice.InputMethodService#onCreateCandidatesView()} when it's ready
to display the candidate view. In your implementation of this method, return a layout that shows
word suggestions, or return null if you don’t want to show anything (a null response is the
default behavior, so you don’t have to implement this if you don’t provide suggestions).</p>
<p>
- For an example implementation that provides user suggestions, see the
- Soft Keyboard <a href="{@docRoot}tools/samples/index.html">sample
+ For an example implementation that provides user suggestions, see the
+ Soft Keyboard <a href="{@docRoot}tools/samples/index.html">sample
app</a>.
</p>
<h3 id="DesignConsiderations">UI design considerations</h3>
@@ -209,10 +224,10 @@
<strong>Figure 2.</strong> Latin IME input types.
</p>
<p>
- When an input field receives focus and your IME starts, the system calls
+ When an input field receives focus and your IME starts, the system calls
{@link android.inputmethodservice.InputMethodService#onStartInputView(EditorInfo, boolean)
- onStartInputView()}, passing in an {@link android.view.inputmethod.EditorInfo} object that
- contains details about the input type and other attributes of the text field. In this object,
+ onStartInputView()}, passing in an {@link android.view.inputmethod.EditorInfo} object that
+ contains details about the input type and other attributes of the text field. In this object,
the {@link android.view.inputmethod.EditorInfo#inputType} field contains the text field's input
type.
</p>
@@ -223,7 +238,7 @@
this:
</p>
<pre>
-inputType & InputType.TYPE_MASK_CLASS
+inputType & InputType.TYPE_MASK_CLASS
</pre>
<p>
The input type bit pattern can have one of several values, including:
@@ -248,7 +263,7 @@
</dd>
</dl>
<p>
- These constants are described in more detail in the reference documentation for
+ These constants are described in more detail in the reference documentation for
{@link android.text.InputType}.
</p>
<p>
@@ -287,8 +302,8 @@
<p>
As the user inputs text with your IME, you can send text to the application by
sending individual key events or by editing the text around the cursor in the application's text
- field. In either case, you use an instance of {@link android.view.inputmethod.InputConnection}
- to deliver the text. To get this instance, call
+ field. In either case, you use an instance of {@link android.view.inputmethod.InputConnection}
+ to deliver the text. To get this instance, call
{@link android.inputmethodservice.InputMethodService#getCurrentInputConnection
InputMethodService.getCurrentInputConnection()}.
</p>
@@ -336,18 +351,18 @@
</p>
<pre>
InputConnection ic = getCurrentInputConnection();
-
+
ic.deleteSurroundingText(4, 0);
-
+
ic.commitText("Hello", 1);
-
+
ic.commitText("!", 1);
</pre>
<h3 id="ComposeThenCommit">Composing text before committing</h3>
<p>
If your IME does text prediction or requires multiple steps to compose a glyph or
word, you can show the progress in the text field until the user commits the word, and then you
- can replace the partial composition with the completed text. You may give special treatment to
+ can replace the partial composition with the completed text. You may give special treatment to
the text by adding a "span" to it when you pass it to InputConnection#setComposingText().
</p>
<p>
@@ -385,10 +400,10 @@
selection during composition. You may also want to trap the back key to dismiss any popups
originating from the input method window.</p>
<p>
- To intercept hardware keys, override
+ To intercept hardware keys, override
{@link android.inputmethodservice.InputMethodService#onKeyDown(int, KeyEvent) onKeyDown()}
- and {@link android.inputmethodservice.InputMethodService#onKeyUp(int, KeyEvent) onKeyUp()}.
- See the Soft Keyboard <a href="{@docRoot}tools/samples/index.html">sample
+ and {@link android.inputmethodservice.InputMethodService#onKeyUp(int, KeyEvent) onKeyUp()}.
+ See the Soft Keyboard <a href="{@docRoot}tools/samples/index.html">sample
app</a> for an example.
</p>
<p>
@@ -396,7 +411,7 @@
</p>
<h2 id="IMESubTypes">Creating an IME Subtype</h2>
<p>
- Subtypes allow the IME to expose multiple input modes and languages supported by an IME. A
+ Subtypes allow the IME to expose multiple input modes and languages supported by an IME. A
subtype can represent:
</p>
<ul>
@@ -414,13 +429,13 @@
<p>
Subtype information is used for an IME switcher dialog that's available from the notification
bar and also for IME settings. The information also allows the framework to bring up a
- specific subtype of an IME directly. When you build an IME, use the subtype facility, because
+ specific subtype of an IME directly. When you build an IME, use the subtype facility, because
it helps the user identify and switch between different IME languages and modes.
</p>
<p>
You define subtypes in one of the input method's XML resource files, using the
- <code><subtype></code> element. The following snippet defines an IME with two
- subtypes: a keyboard subtype for the US English locale, and another keyboard subtype for the
+ <code><subtype></code> element. The following snippet defines an IME with two
+ subtypes: a keyboard subtype for the US English locale, and another keyboard subtype for the
French language locale for France:
</p>
<pre>
@@ -457,8 +472,8 @@
android:imeSubtypeMode="keyboard" />
</pre>
<p>
- The next snippet is part of the IME's <code>strings.xml</code> file. The string
- resource <code>label_subtype_generic</code>, which is used by the input method UI definition to
+ The next snippet is part of the IME's <code>strings.xml</code> file. The string
+ resource <code>label_subtype_generic</code>, which is used by the input method UI definition to
set the subtype's label, is defined as:
</p>
<pre>
@@ -487,9 +502,9 @@
<h3 id="SubtypeSettings">Choosing IME subtypes from System Settings</h3>
<p>
A user can control how subtypes are used in the “Language & input” settings panel in the
- System Settings area. In the Soft Keyboard sample, the file
- <code>InputMethodSettingsFragment.java</code> contains an implementation that
- facilitates a subtype enabler in the IME settings. Please refer to the SoftKeyboard sample in
+ System Settings area. In the Soft Keyboard sample, the file
+ <code>InputMethodSettingsFragment.java</code> contains an implementation that
+ facilitates a subtype enabler in the IME settings. Please refer to the SoftKeyboard sample in
the Android SDK for more information about how to support Input Method Subtypes in your IME.
</p>
<img src="{@docRoot}resources/articles/images/inputmethod_subtype_settings.png" alt=""
@@ -497,6 +512,7 @@
<p class="img-caption">
<strong>Figure 6.</strong> Choosing a language for the IME.
</p>
+
<h2 id="GeneralDesign">General IME Considerations</h2>
<p>
Here are some other things to consider as you're implementing your IME:
@@ -506,22 +522,22 @@
Provide a way for users to set options directly from the IME's UI.
</li>
<li>
- Because multiple IMEs may be installed on the device, provide a way for the user to switch to a
+ Because multiple IMEs may be installed on the device, provide a way for the user to switch to a
different IME directly from the input method UI.
</li>
<li>
- Bring up the IME's UI quickly. Preload or load on demand any large resources so that users
- see the IME as soon as they tap on a text field. Cache resources and views for subsequent
+ Bring up the IME's UI quickly. Preload or load on demand any large resources so that users
+ see the IME as soon as they tap on a text field. Cache resources and views for subsequent
invocations of the input method.
</li>
<li>
- Conversely, you should release large memory allocations soon after the input method window is
+ Conversely, you should release large memory allocations soon after the input method window is
hidden, so that applications can have sufficient memory to run. Consider using a delayed message
to release resources if the IME is in a hidden state for a few seconds.
-</li>
+</li>
<li>
- Make sure that users can enter as many characters as possible for the language or locale
- associated with the IME. Remember that users may use punctuation in passwords or user
+ Make sure that users can enter as many characters as possible for the language or locale
+ associated with the IME. Remember that users may use punctuation in passwords or user
names, so your IME has to provide many different characters to allow users to enter a
password and get access to the device.
</li>
diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js
index a4302ef..62d18d6 100644
--- a/docs/html/jd_extras.js
+++ b/docs/html/jd_extras.js
@@ -57,9 +57,9 @@
"type":"video"
},
{
- "title":"5 Tips for Succeeding",
+ "title":"Plan for Success",
"titleFriendly":"",
- "summary":"See inspirational ways of using technology in the classroom.",
+ "summary":"5 tips from developers on creating great EDU apps.",
"url":"https://www.youtube.com/watch?v=Eh2adsAyTKc",
"group":"",
"keywords": [],
diff --git a/docs/html/preview/material/ui-widgets.jd b/docs/html/preview/material/ui-widgets.jd
index 2d29420..69b7d2d 100644
--- a/docs/html/preview/material/ui-widgets.jd
+++ b/docs/html/preview/material/ui-widgets.jd
@@ -132,7 +132,7 @@
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.my_text_view, parent);
+ .inflate(R.layout.my_text_view, parent, false);
// set the view's size, margins, paddings and layout parameters
...
ViewHolder vh = new ViewHolder(v);
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 6e98f5e..2bbdfc8c 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -306,6 +306,9 @@
<p>
Android Studio is a new IDE powered by IntelliJ that provides new features and improvements
over ADT. It's currently in beta but will be the official Android IDE once it's ready.</p>
+ <p>
+ If you're a new Android developer, you should consider starting with Android Studio, because the
+ ADT plugin for Eclipse is no longer in active development.</p>
<p style="margin: 0;">
<a href="/sdk/installing/studio.html">Learn more about Android Studio</a></p>
</div>
diff --git a/docs/html/sdk/installing/installing-adt.jd b/docs/html/sdk/installing/installing-adt.jd
index 88bc733..864e82e 100644
--- a/docs/html/sdk/installing/installing-adt.jd
+++ b/docs/html/sdk/installing/installing-adt.jd
@@ -28,8 +28,7 @@
<li>Eclipse <a href="http://www.eclipse.org/jdt">JDT</a> plugin (included
in most Eclipse IDE packages) </li>
<li><a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html">JDK 6</a>
- (JRE alone is not sufficient)</li>oid Development Tools plugin</a>
-(recommended)</li>
+ (JRE alone is not sufficient)</li>
<li><strong>Not</strong> compatible with GNU Compiler for Java (gcj)</li>
</ul>
@@ -79,73 +78,6 @@
<a href="{@docRoot}sdk/installing/adding-packages.html">Adding Platforms and Packages</a>.</p>
-<h2 id="tmgr">Download the ADT Translation Manager Plugin</h2>
-
-<div class="sidebox-wrapper">
-<div class="sidebox">
-<h2>App Translations in Google Play</h2>
-<p>Google Play <a href="{@docRoot}distribute/tools/localization-checklist.html#gp-trans">App
-Translation Service</a> is available in the Developer Console to help you
-localize your app for a global user base. You can browse qualified vendors, get
-estimates, upload strings for translation, and then import the translations directly
-into your app.</p>
-</div>
-</div>
-
-<p>ADT Translation Manager Plugin is an Android SDK Tools plugin that helps
-you work with strings that you are localizing. It's designed to work
-with the translation services built into the Google Play Developer
-Console that let you quickly find and purchase translations of
-your app from a list of pre-qualified vendors. </p>
-
-<p>The plugin simplifies the management of strings
-during translation. It lets you easily export your default strings
-and upload them directly to the Developer Console, from which you
-can get estimates or purchase translations. When your translations
-are ready, the plugin lets you quickly download and import them
-back into your project. During import, it moves all of the translated resources into
-the correct locations under <code>res/values</code>, so that
-localization works instantly.</p>
-
-<p>For more information about translation services in Google Play, see <a
-href="{@docRoot}distribute/tools/localization-checklist.html#gp-trans">Purchase
-professional translations through the Developer Console</a>.</p>
-
-<p>To install the ADT Translation Manager Plugin follow these steps:</p>
-
-<ol>
- <li>Install the ADT Plugin, as described above. </li>
- <li>In Eclipse, select <strong>Help</strong> > <strong>Install New
-Software</strong>.</li>
- <li>Click <strong>Add</strong>, in the top-right corner.</li>
- <li>In the Add Repository dialog that appears, enter a repository name for the <em>Name</em>
- and the following URL for the <em>Location</em>:
- <pre>https://dl.google.com/alt/</pre>
- </li>
- <li>Click <strong>OK</strong>.
- <li>In the Available Software dialog, select the checkbox next to <strong>Android Developer Tools
- - Translation Manager</strong> and click <strong>Next</strong>.</li>
- <li>In the next window, you'll see a list of the tools to be downloaded. Click
-<strong>Next</strong>. </li>
- <li>Read and accept the license agreements, then click <strong>Finish</strong>.
- <p>If you get a security warning saying that the authenticity or validity of
-the software can't be established, click <strong>OK</strong>.</p></li>
- <li>When the installation completes, restart Eclipse. </li>
-</ol>
-
-<h4 id="translation-manager-notes">Installation notes</h4>
-
-<ul>
-<li>The full ADT Plugin must be installed in your Eclipse environment before you install the ADT Translation Manager Plugin.</li>
-<li>ADT Translation Manager Plugin is designed for use with the translation services offered through the Google Play Developer Console. It is not designed for general purpose import/export of strings. </li>
-<li>To use the plugin, you must <a href="{@docRoot}distribute/googleplay/start.html">set up a Developer Console account</a>. </li>
-<li>Currently, translation services are available through the Developer Console only as part of a pilot program. To use the plugin, you must first sign up for the pilot program by visiting the Developer Console.</li>
-<li>If you downloaded ADT as part of the SDK ADT bundle, you may encounter an error when attempting to download the ADT Translation Manager Plugin from the remote repository. In that case, open the <strong>Install New
-Software</strong>, uncheck "Contact all update sites during install to find required software" at the bottom and try again. </li>
-</ul>
-
-
-
<h2 id="Troubleshooting">Troubleshooting ADT Installation</h2>
<p>If you are having trouble downloading the ADT plugin after following the
diff --git a/docs/html/training/implementing-navigation/ancestral.jd b/docs/html/training/implementing-navigation/ancestral.jd
index 57251c1..8aa0fd6 100644
--- a/docs/html/training/implementing-navigation/ancestral.jd
+++ b/docs/html/training/implementing-navigation/ancestral.jd
@@ -133,7 +133,25 @@
navigateUpFromSameTask()}. When you call this method, it finishes the current activity and
starts (or resumes) the appropriate parent activity.
If the target parent activity is in the task's back stack, it is brought
-forward as defined by {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}.</p>
+forward. The way it is brought forward depends on whether the parent activity
+is able to handle an {@link
+android.app.Activity#onNewIntent onNewIntent()} call:</p>
+
+<ul>
+ <li>If the parent activity has launch mode <a
+ href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code
+ <singleTop>}</a>, or the <code>up</code> intent contains {@link
+ android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}, the parent activity is
+ brought to the top of the stack, and receives the intent through its
+ {@link android.app.Activity#onNewIntent onNewIntent()} method.</li>
+
+ <li>If the parent activity has launch mode <a
+ href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code
+ <standard>}</a>, and the <code>up</code> intent does not contain
+ {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}, the parent activity
+ is popped off the stack, and a new instance of that activity is created on
+ top of the stack to receive the intent.</li>
+</ul>
<p>For example:</p>
@@ -157,7 +175,6 @@
navigating <em>Up</em> should create a new task that belongs to your app, which
requires that you create a new back stack.</p>
-
<h3 id="BuildBackStack">Navigate up with a new back stack</h3>
<p>If your activity provides any <a
diff --git a/docs/html/training/notepad/index.jd b/docs/html/training/notepad/index.jd
index d1aa221..507b232 100644
--- a/docs/html/training/notepad/index.jd
+++ b/docs/html/training/notepad/index.jd
@@ -17,13 +17,9 @@
application. The exercises explain each step in detail and provide all the
sample code you need to complete the application. </p>
-<p>When you are finished with the tutorial, you will have created a functioning
+<p><p>When you are finished with the tutorial, you will have created a functioning
Android application and will have learned many of the most important
-concepts in Android development. If you want to add more complex features to
-your application, you can examine the code in an alternative implementation
-of a Note Pad application, in the
-<a href="{@docRoot}resources/samples/index.html">Sample Code</a> section. </p>
-
+concepts in Android development.</p>
<a name="who"></a>
<h2>Who Should Use this Tutorial</h2>
@@ -122,17 +118,13 @@
<li>For a lighter but broader introduction to concepts not covered in the
tutorial,
take a look at <a href="{@docRoot}resources/faq/commontasks.html">Common Android Tasks</a>.</li>
-<li>The Android SDK includes a variety of fully functioning sample applications
-that make excellent opportunities for further learning. You can find the sample
-applications in the <code>samples/</code> directory of your downloaded SDK, or browser them
-here, in the <a href="{@docRoot}resources/samples/index.html">Sample Code</a> section.</li>
+
<li>This tutorial draws from the full Notepad application included in the
-<code>samples/</code> directory of the SDK, though it does not match it exactly.
-When you are done with the tutorial,
-it is highly recommended that you take a closer look at this version of the Notepad
-application,
-as it demonstrates a variety of interesting additions for your application,
-such as:</li>
+<code>samples/legacy/NotePad</code> directory of the SDK, though it does not
+match it exactly. When you are done with the tutorial, it is highly recommended
+that you take a closer look at this version of the Notepad application, as it
+demonstrates a variety of interesting additions for your application, such
+as:</li>
<ul>
<li>Setting up a custom striped list for the list of notes.</li>
<li>Creating a custom text edit view that overrides the <code>draw()</code>
diff --git a/docs/html/training/wearables/apps/voice.jd b/docs/html/training/wearables/apps/voice.jd
index 3dea5d7..639d90a 100644
--- a/docs/html/training/wearables/apps/voice.jd
+++ b/docs/html/training/wearables/apps/voice.jd
@@ -263,7 +263,7 @@
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
- if (requestCode == SPEECH_REQUEST && resultCode == RESULT_OK) {
+ if (requestCode == SPEECH_REQUEST_CODE && resultCode == RESULT_OK) {
List<String> results = data.getStringArrayListExtra(
RecognizerIntent.EXTRA_RESULTS);
String spokenText = results.get(0);
@@ -271,4 +271,4 @@
}
super.onActivityResult(requestCode, resultCode, data);
}
-</pre>
\ No newline at end of file
+</pre>
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index 3ab57c1..5e004a3 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -43,7 +43,7 @@
mTileX = tileX;
mTileY = tileY;
final long b = bitmap.ni();
- native_instance = nativeCreate(b, tileX.nativeInt, tileY.nativeInt);
+ init(nativeCreate(b, tileX.nativeInt, tileY.nativeInt));
}
/**
diff --git a/graphics/java/android/graphics/BlurMaskFilter.java b/graphics/java/android/graphics/BlurMaskFilter.java
index 939af52..f3064f8 100644
--- a/graphics/java/android/graphics/BlurMaskFilter.java
+++ b/graphics/java/android/graphics/BlurMaskFilter.java
@@ -25,10 +25,25 @@
public class BlurMaskFilter extends MaskFilter {
public enum Blur {
- NORMAL(0), //!< blur inside and outside of the original border
- SOLID(1), //!< include the original mask, blur outside
- OUTER(2), //!< just blur outside the original border
- INNER(3); //!< just blur inside the original border
+ /**
+ * Blur inside and outside the original border.
+ */
+ NORMAL(0),
+
+ /**
+ * Draw solid inside the border, blur outside.
+ */
+ SOLID(1),
+
+ /**
+ * Draw nothing inside the border, blur outside.
+ */
+ OUTER(2),
+
+ /**
+ * Blur inside the border, draw nothing outside.
+ */
+ INNER(3);
Blur(int value) {
native_int = value;
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 72e2056..ef4b260 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -158,7 +158,7 @@
if (nativeCanvas == 0) {
throw new IllegalStateException();
}
- mNativeCanvasWrapper = initCanvas(nativeCanvas);
+ mNativeCanvasWrapper = nativeCanvas;
mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
mDensity = Bitmap.getDefaultDensity();
}
@@ -924,7 +924,7 @@
* @param b blue component (0..255) of the color to draw onto the canvas
*/
public void drawRGB(int r, int g, int b) {
- native_drawRGB(mNativeCanvasWrapper, r, g, b);
+ drawColor(Color.rgb(r, g, b));
}
/**
@@ -937,7 +937,7 @@
* @param b blue component (0..255) of the color to draw onto the canvas
*/
public void drawARGB(int a, int r, int g, int b) {
- native_drawARGB(mNativeCanvasWrapper, a, r, g, b);
+ drawColor(Color.argb(a, r, g, b));
}
/**
@@ -947,7 +947,7 @@
* @param color the color to draw onto the canvas
*/
public void drawColor(int color) {
- native_drawColor(mNativeCanvasWrapper, color);
+ native_drawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
}
/**
@@ -1301,13 +1301,28 @@
*/
public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,
@Nullable Paint paint) {
- if (dst == null) {
- throw new NullPointerException();
- }
- throwIfCannotDraw(bitmap);
- native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
- paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
- }
+ if (dst == null) {
+ throw new NullPointerException();
+ }
+ throwIfCannotDraw(bitmap);
+ final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+
+ float left, top, right, bottom;
+ if (src == null) {
+ left = top = 0;
+ right = bitmap.getWidth();
+ bottom = bitmap.getHeight();
+ } else {
+ left = src.left;
+ right = src.right;
+ top = src.top;
+ bottom = src.bottom;
+ }
+
+ native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), left, top, right, bottom,
+ dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
+ bitmap.mDensity);
+ }
/**
* Draw the specified bitmap, scaling/translating automatically to fill
@@ -1337,8 +1352,23 @@
throw new NullPointerException();
}
throwIfCannotDraw(bitmap);
- native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
- paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
+ final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+
+ int left, top, right, bottom;
+ if (src == null) {
+ left = top = 0;
+ right = bitmap.getWidth();
+ bottom = bitmap.getHeight();
+ } else {
+ left = src.left;
+ right = src.right;
+ top = src.top;
+ bottom = src.bottom;
+ }
+
+ native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), left, top, right, bottom,
+ dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
+ bitmap.mDensity);
}
/**
@@ -1866,7 +1896,6 @@
public static native void freeTextLayoutCaches();
private static native long initRaster(long nativeBitmapOrZero);
- private static native long initCanvas(long canvasHandle);
private static native void native_setBitmap(long canvasHandle,
long bitmapHandle,
boolean copyState);
@@ -1919,11 +1948,6 @@
private static native boolean native_quickReject(long nativeCanvas,
float left, float top,
float right, float bottom);
- private static native void native_drawRGB(long nativeCanvas, int r, int g,
- int b);
- private static native void native_drawARGB(long nativeCanvas, int a, int r,
- int g, int b);
- private static native void native_drawColor(long nativeCanvas, int color);
private static native void native_drawColor(long nativeCanvas, int color,
int mode);
private static native void native_drawPaint(long nativeCanvas,
@@ -1965,16 +1989,9 @@
int screenDensity,
int bitmapDensity);
private native void native_drawBitmap(long nativeCanvas, long nativeBitmap,
- Rect src, RectF dst,
- long nativePaintOrZero,
- int screenDensity,
- int bitmapDensity);
- private static native void native_drawBitmap(long nativeCanvas,
- long nativeBitmap,
- Rect src, Rect dst,
- long nativePaintOrZero,
- int screenDensity,
- int bitmapDensity);
+ float srcLeft, float srcTop, float srcRight, float srcBottom,
+ float dstLeft, float dstTop, float dstRight, float dstBottom,
+ long nativePaintOrZero, int screenDensity, int bitmapDensity);
private static native void native_drawBitmap(long nativeCanvas, int[] colors,
int offset, int stride, float x,
float y, int width, int height,
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index d7b2071..b2adcf6 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -53,8 +53,8 @@
mShaderA = shaderA;
mShaderB = shaderB;
mXferMode = mode;
- native_instance = nativeCreate1(shaderA.native_instance, shaderB.native_instance,
- (mode != null) ? mode.native_instance : 0);
+ init(nativeCreate1(shaderA.getNativeInstance(), shaderB.getNativeInstance(),
+ (mode != null) ? mode.native_instance : 0));
}
/** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
@@ -69,8 +69,8 @@
mShaderA = shaderA;
mShaderB = shaderB;
mPorterDuffMode = mode;
- native_instance = nativeCreate2(shaderA.native_instance, shaderB.native_instance,
- mode.nativeInt);
+ init(nativeCreate2(shaderA.getNativeInstance(), shaderB.getNativeInstance(),
+ mode.nativeInt));
}
/**
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 90cb217..7b1145c 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -65,7 +65,7 @@
mColors = colors;
mPositions = positions;
mTileMode = tile;
- native_instance = nativeCreate1(x0, y0, x1, y1, colors, positions, tile.nativeInt);
+ init(nativeCreate1(x0, y0, x1, y1, colors, positions, tile.nativeInt));
}
/** Create a shader that draws a linear gradient along a line.
@@ -87,7 +87,7 @@
mColor0 = color0;
mColor1 = color1;
mTileMode = tile;
- native_instance = nativeCreate2(x0, y0, x1, y1, color0, color1, tile.nativeInt);
+ init(nativeCreate2(x0, y0, x1, y1, color0, color1, tile.nativeInt));
}
/**
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 1709558..3a4c2a7 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -155,6 +155,11 @@
* Passing a zero radius is equivalent to calling {@link #setRect(int, int, int, int)}
*/
public void setRoundRect(int left, int top, int right, int bottom, float radius) {
+ if (left >= right || top >= bottom) {
+ setEmpty();
+ return;
+ }
+
if (mRect == null) mRect = new Rect();
mRect.set(left, top, right, bottom);
mRadius = radius;
@@ -173,11 +178,17 @@
* Sets the outline to the oval defined by input rect.
*/
public void setOval(int left, int top, int right, int bottom) {
+ if (left >= right || top >= bottom) {
+ setEmpty();
+ return;
+ }
+
if ((bottom - top) == (right - left)) {
// represent circle as round rect, for efficiency, and to enable clipping
setRoundRect(left, top, right, bottom, (bottom - top) / 2.0f);
return;
}
+
if (mPath == null) mPath = new Path();
mPath.reset();
mPath.addOval(left, top, right, bottom, Path.Direction.CW);
@@ -196,6 +207,11 @@
* Sets the Constructs an Outline from a {@link android.graphics.Path#isConvex() convex path}.
*/
public void setConvexPath(@NonNull Path convexPath) {
+ if (convexPath.isEmpty()) {
+ setEmpty();
+ return;
+ }
+
if (!convexPath.isConvex()) {
throw new IllegalArgumentException("path must be convex");
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 72e39bb..58a1bf2 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -919,7 +919,7 @@
public Shader setShader(Shader shader) {
long shaderNative = 0;
if (shader != null)
- shaderNative = shader.native_instance;
+ shaderNative = shader.getNativeInstance();
native_setShader(mNativePaint, shaderNative);
mShader = shader;
return shader;
@@ -1260,6 +1260,29 @@
public native void setTextSkewX(float skewX);
/**
+ * Return the paint's letter-spacing for text. The default value
+ * is 0.
+ *
+ * @return the paint's letter-spacing for drawing text.
+ * @hide
+ */
+ public float getLetterSpacing() {
+ return native_getLetterSpacing(mNativePaint);
+ }
+
+ /**
+ * Set the paint's letter-spacing for text. The default value
+ * is 0. The value is in 'EM' units. Typical values for slight
+ * expansion will be around 0.05. Negative values tighten text.
+ *
+ * @param letterSpacing set the paint's letter-spacing for drawing text.
+ * @hide
+ */
+ public void setLetterSpacing(float letterSpacing) {
+ native_setLetterSpacing(mNativePaint, letterSpacing);
+ }
+
+ /**
* Return the distance above (negative) the baseline (ascent) based on the
* current typeface and text size.
*
@@ -2232,4 +2255,8 @@
private static native void native_setShadowLayer(long native_object,
float radius, float dx, float dy, int color);
private static native boolean native_hasShadowLayer(long native_object);
+
+ private static native float native_getLetterSpacing(long native_object);
+ private static native void native_setLetterSpacing(long native_object,
+ float letterSpacing);
}
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index 75c951a..9d015f7 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -65,7 +65,7 @@
mColors = colors;
mPositions = positions;
mTileMode = tile;
- native_instance = nativeCreate1(x, y, radius, colors, positions, tile.nativeInt);
+ init(nativeCreate1(x, y, radius, colors, positions, tile.nativeInt));
}
/** Create a shader that draws a radial gradient given the center and radius.
@@ -88,7 +88,7 @@
mColor0 = color0;
mColor1 = color1;
mTileMode = tile;
- native_instance = nativeCreate2(x, y, radius, color0, color1, tile.nativeInt);
+ init(nativeCreate2(x, y, radius, color0, color1, tile.nativeInt));
}
/**
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 936fe0c..265a564 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -25,10 +25,19 @@
public class Shader {
/**
* This is set by subclasses, but don't make it public.
- *
- * @hide
*/
- public long native_instance;
+ private long native_instance;
+
+ private long native_with_local_matrix;
+
+ /**
+ * Initialization step that should be called by subclasses in their
+ * constructors. Calling again may result in memory leaks.
+ * @hide
+ */
+ protected void init(long ni) {
+ native_instance = ni;
+ }
private Matrix mLocalMatrix;
@@ -73,21 +82,22 @@
*
* Starting with {@link android.os.Build.VERSION_CODES#L}, this does not
* modify any Paints which use this Shader. In order to modify the Paint,
- * you need to call {@link Paint#setShader} again.
+ * you need to call {@link Paint#setShader} again. Further, any {@link ComposeShader}s
+ * created with this Shader will be unaffected.
*
* @param localM The shader's new local matrix, or null to specify identity
*/
public void setLocalMatrix(Matrix localM) {
mLocalMatrix = localM;
- native_instance = nativeSetLocalMatrix(native_instance,
- localM == null ? 0 : localM.native_instance);
+ native_with_local_matrix = nativeSetLocalMatrix(native_instance,
+ native_with_local_matrix, localM == null ? 0 : localM.native_instance);
}
protected void finalize() throws Throwable {
try {
super.finalize();
} finally {
- nativeDestructor(native_instance);
+ nativeDestructor(native_instance, native_with_local_matrix);
}
}
@@ -113,7 +123,14 @@
}
}
- private static native void nativeDestructor(long native_shader);
+ /* package */ long getNativeInstance() {
+ if (native_with_local_matrix != 0) {
+ return native_with_local_matrix;
+ }
+ return native_instance;
+ }
+
+ private static native void nativeDestructor(long native_shader, long native_with_local_matrix);
private static native long nativeSetLocalMatrix(long native_shader,
- long matrix_instance);
+ long native_with_local_matrix, long matrix_instance);
}
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index 18a748f..008891d 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -62,7 +62,7 @@
mCy = cy;
mColors = colors;
mPositions = positions;
- native_instance = nativeCreate1(cx, cy, colors, positions);
+ init(nativeCreate1(cx, cy, colors, positions));
}
/**
@@ -79,7 +79,7 @@
mCy = cy;
mColor0 = color0;
mColor1 = color1;
- native_instance = nativeCreate2(cx, cy, color0, color1);
+ init(nativeCreate2(cx, cy, color0, color1));
}
/**
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index db5c8e3..525e01d 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -386,6 +386,7 @@
*
* @see #setTileModeY(android.graphics.Shader.TileMode)
* @see #setTileModeXY(android.graphics.Shader.TileMode, android.graphics.Shader.TileMode)
+ * @attr ref android.R.styleable#BitmapDrawable_tileModeX
*/
public void setTileModeX(Shader.TileMode mode) {
setTileModeXY(mode, mBitmapState.mTileModeY);
@@ -401,6 +402,7 @@
*
* @see #setTileModeX(android.graphics.Shader.TileMode)
* @see #setTileModeXY(android.graphics.Shader.TileMode, android.graphics.Shader.TileMode)
+ * @attr ref android.R.styleable#BitmapDrawable_tileModeY
*/
public final void setTileModeY(Shader.TileMode mode) {
setTileModeXY(mBitmapState.mTileModeX, mode);
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 2d49365..6f21f2e 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -28,7 +28,6 @@
import android.util.MathUtils;
import android.view.HardwareCanvas;
import android.view.RenderNodeAnimator;
-import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
@@ -44,16 +43,14 @@
private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024.0f * GLOBAL_SPEED;
private static final float WAVE_TOUCH_UP_ACCELERATION = 3400.0f * GLOBAL_SPEED;
private static final float WAVE_OPACITY_DECAY_VELOCITY = 3.0f / GLOBAL_SPEED;
- private static final float WAVE_OUTER_OPACITY_VELOCITY_MAX = 4.5f * GLOBAL_SPEED;
- private static final float WAVE_OUTER_OPACITY_VELOCITY_MIN = 1.5f * GLOBAL_SPEED;
- private static final float WAVE_OUTER_SIZE_INFLUENCE_MAX = 200f;
- private static final float WAVE_OUTER_SIZE_INFLUENCE_MIN = 40f;
private static final long RIPPLE_ENTER_DELAY = 80;
// Hardware animators.
- private final ArrayList<RenderNodeAnimator> mRunningAnimations = new ArrayList<>();
- private final ArrayList<RenderNodeAnimator> mPendingAnimations = new ArrayList<>();
+ private final ArrayList<RenderNodeAnimator> mRunningAnimations =
+ new ArrayList<RenderNodeAnimator>();
+ private final ArrayList<RenderNodeAnimator> mPendingAnimations =
+ new ArrayList<RenderNodeAnimator>();
private final RippleDrawable mOwner;
@@ -79,20 +76,17 @@
private CanvasProperty<Float> mPropRadius;
private CanvasProperty<Float> mPropX;
private CanvasProperty<Float> mPropY;
- private CanvasProperty<Paint> mPropOuterPaint;
- private CanvasProperty<Float> mPropOuterRadius;
- private CanvasProperty<Float> mPropOuterX;
- private CanvasProperty<Float> mPropOuterY;
// Software animators.
private ObjectAnimator mAnimRadius;
private ObjectAnimator mAnimOpacity;
- private ObjectAnimator mAnimOuterOpacity;
private ObjectAnimator mAnimX;
private ObjectAnimator mAnimY;
+ // Temporary paint used for creating canvas properties.
+ private Paint mTempPaint;
+
// Software rendering properties.
- private float mOuterOpacity = 0;
private float mOpacity = 1;
private float mOuterX;
private float mOuterY;
@@ -177,38 +171,35 @@
return mOpacity;
}
- public void setOuterOpacity(float a) {
- mOuterOpacity = a;
- invalidateSelf();
- }
-
- public float getOuterOpacity() {
- return mOuterOpacity;
- }
-
+ @SuppressWarnings("unused")
public void setRadiusGravity(float r) {
mTweenRadius = r;
invalidateSelf();
}
+ @SuppressWarnings("unused")
public float getRadiusGravity() {
return mTweenRadius;
}
+ @SuppressWarnings("unused")
public void setXGravity(float x) {
mTweenX = x;
invalidateSelf();
}
+ @SuppressWarnings("unused")
public float getXGravity() {
return mTweenX;
}
+ @SuppressWarnings("unused")
public void setYGravity(float y) {
mTweenY = y;
invalidateSelf();
}
+ @SuppressWarnings("unused")
public float getYGravity() {
return mTweenY;
}
@@ -238,7 +229,7 @@
// If we have any pending hardware animations, cancel any running
// animations and start those now.
final ArrayList<RenderNodeAnimator> pendingAnimations = mPendingAnimations;
- final int N = pendingAnimations == null ? 0 : pendingAnimations.size();
+ final int N = pendingAnimations.size();
if (N > 0) {
cancelHardwareAnimations();
@@ -251,7 +242,6 @@
pendingAnimations.clear();
}
- c.drawCircle(mPropOuterX, mPropOuterY, mPropOuterRadius, mPropOuterPaint);
c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
return true;
@@ -262,15 +252,6 @@
// Cache the paint alpha so we can restore it later.
final int paintAlpha = p.getAlpha();
-
- final int outerAlpha = (int) (paintAlpha * mOuterOpacity + 0.5f);
- if (outerAlpha > 0 && mOuterRadius > 0) {
- p.setAlpha(outerAlpha);
- p.setStyle(Style.FILL);
- c.drawCircle(mOuterX, mOuterY, mOuterRadius, p);
- hasContent = true;
- }
-
final int alpha = (int) (paintAlpha * mOpacity + 0.5f);
final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
if (alpha > 0 && radius > 0) {
@@ -316,7 +297,6 @@
public void enter() {
final int radiusDuration = (int)
(1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5);
- final int outerDuration = (int) (1000 * 1.0f / WAVE_OUTER_OPACITY_VELOCITY_MIN);
final ObjectAnimator radius = ObjectAnimator.ofFloat(this, "radiusGravity", 1);
radius.setAutoCancel(true);
@@ -336,13 +316,7 @@
cY.setInterpolator(LINEAR_INTERPOLATOR);
cY.setStartDelay(RIPPLE_ENTER_DELAY);
- final ObjectAnimator outer = ObjectAnimator.ofFloat(this, "outerOpacity", 0, 1);
- outer.setAutoCancel(true);
- outer.setDuration(outerDuration);
- outer.setInterpolator(LINEAR_INTERPOLATOR);
-
mAnimRadius = radius;
- mAnimOuterOpacity = outer;
mAnimX = cX;
mAnimY = cY;
@@ -350,7 +324,6 @@
// that anything interesting is happening until the user lifts their
// finger.
radius.start();
- outer.start();
cX.start();
cY.start();
}
@@ -372,51 +345,23 @@
+ WAVE_TOUCH_DOWN_ACCELERATION) * mDensity) + 0.5);
final int opacityDuration = (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
- // Scale the outer max opacity and opacity velocity based
- // on the size of the outer radius
-
- float outerSizeInfluence = MathUtils.constrain(
- (mOuterRadius - WAVE_OUTER_SIZE_INFLUENCE_MIN * mDensity)
- / (WAVE_OUTER_SIZE_INFLUENCE_MAX * mDensity), 0, 1);
- float outerOpacityVelocity = MathUtils.lerp(WAVE_OUTER_OPACITY_VELOCITY_MIN,
- WAVE_OUTER_OPACITY_VELOCITY_MAX, outerSizeInfluence);
-
- // Determine at what time the inner and outer opacity intersect.
- // inner(t) = mOpacity - t * WAVE_OPACITY_DECAY_VELOCITY / 1000
- // outer(t) = mOuterOpacity + t * WAVE_OUTER_OPACITY_VELOCITY / 1000
-
- final int outerInflection = Math.max(0, (int) (1000 * (mOpacity - mOuterOpacity)
- / (WAVE_OPACITY_DECAY_VELOCITY + outerOpacityVelocity) + 0.5f));
- final int inflectionOpacity = (int) (255 * (mOuterOpacity + outerInflection
- * outerOpacityVelocity * outerSizeInfluence / 1000) + 0.5f);
-
if (mCanUseHardware) {
- exitHardware(radiusDuration, opacityDuration, outerInflection, inflectionOpacity);
+ exitHardware(radiusDuration, opacityDuration);
} else {
- exitSoftware(radiusDuration, opacityDuration, outerInflection, inflectionOpacity);
+ exitSoftware(radiusDuration, opacityDuration);
}
}
- private void exitHardware(int radiusDuration, int opacityDuration, int outerInflection,
- int inflectionOpacity) {
+ private void exitHardware(int radiusDuration, int opacityDuration) {
mPendingAnimations.clear();
final float startX = MathUtils.lerp(
mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
final float startY = MathUtils.lerp(
mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
- final Paint outerPaint = new Paint();
- outerPaint.setAntiAlias(true);
- outerPaint.setColor(mColor);
- outerPaint.setAlpha((int) (255 * mOuterOpacity + 0.5f));
- outerPaint.setStyle(Style.FILL);
- mPropOuterPaint = CanvasProperty.createPaint(outerPaint);
- mPropOuterRadius = CanvasProperty.createFloat(mOuterRadius);
- mPropOuterX = CanvasProperty.createFloat(mOuterX);
- mPropOuterY = CanvasProperty.createFloat(mOuterY);
final float startRadius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
- final Paint paint = new Paint();
+ final Paint paint = getTempPaint();
paint.setAntiAlias(true);
paint.setColor(mColor);
paint.setAlpha((int) (255 * mOpacity + 0.5f));
@@ -442,41 +387,10 @@
RenderNodeAnimator.PAINT_ALPHA, 0);
opacityAnim.setDuration(opacityDuration);
opacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
-
- final RenderNodeAnimator outerOpacityAnim;
- if (outerInflection > 0) {
- // Outer opacity continues to increase for a bit.
- outerOpacityAnim = new RenderNodeAnimator(
- mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, inflectionOpacity);
- outerOpacityAnim.setDuration(outerInflection);
- outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
-
- // Chain the outer opacity exit animation.
- final int outerDuration = opacityDuration - outerInflection;
- if (outerDuration > 0) {
- final RenderNodeAnimator outerFadeOutAnim = new RenderNodeAnimator(
- mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
- outerFadeOutAnim.setDuration(outerDuration);
- outerFadeOutAnim.setInterpolator(LINEAR_INTERPOLATOR);
- outerFadeOutAnim.setStartDelay(outerInflection);
- outerFadeOutAnim.setStartValue(inflectionOpacity);
- outerFadeOutAnim.addListener(mAnimationListener);
-
- mPendingAnimations.add(outerFadeOutAnim);
- } else {
- outerOpacityAnim.addListener(mAnimationListener);
- }
- } else {
- outerOpacityAnim = new RenderNodeAnimator(
- mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
- outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
- outerOpacityAnim.setDuration(opacityDuration);
- outerOpacityAnim.addListener(mAnimationListener);
- }
+ opacityAnim.addListener(mAnimationListener);
mPendingAnimations.add(radiusAnim);
mPendingAnimations.add(opacityAnim);
- mPendingAnimations.add(outerOpacityAnim);
mPendingAnimations.add(xAnim);
mPendingAnimations.add(yAnim);
@@ -485,8 +399,14 @@
invalidateSelf();
}
- private void exitSoftware(int radiusDuration, int opacityDuration, int outerInflection,
- int inflectionOpacity) {
+ private Paint getTempPaint() {
+ if (mTempPaint == null) {
+ mTempPaint = new Paint();
+ }
+ return mTempPaint;
+ }
+
+ private void exitSoftware(int radiusDuration, int opacityDuration) {
final ObjectAnimator radiusAnim = ObjectAnimator.ofFloat(this, "radiusGravity", 1);
radiusAnim.setAutoCancel(true);
radiusAnim.setDuration(radiusDuration);
@@ -506,58 +426,15 @@
opacityAnim.setAutoCancel(true);
opacityAnim.setDuration(opacityDuration);
opacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
-
- final ObjectAnimator outerOpacityAnim;
- if (outerInflection > 0) {
- // Outer opacity continues to increase for a bit.
- outerOpacityAnim = ObjectAnimator.ofFloat(this,
- "outerOpacity", inflectionOpacity / 255.0f);
- outerOpacityAnim.setAutoCancel(true);
- outerOpacityAnim.setDuration(outerInflection);
- outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
-
- // Chain the outer opacity exit animation.
- final int outerDuration = opacityDuration - outerInflection;
- if (outerDuration > 0) {
- outerOpacityAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- final ObjectAnimator outerFadeOutAnim = ObjectAnimator.ofFloat(Ripple.this,
- "outerOpacity", 0);
- outerFadeOutAnim.setAutoCancel(true);
- outerFadeOutAnim.setDuration(outerDuration);
- outerFadeOutAnim.setInterpolator(LINEAR_INTERPOLATOR);
- outerFadeOutAnim.addListener(mAnimationListener);
-
- mAnimOuterOpacity = outerFadeOutAnim;
-
- outerFadeOutAnim.start();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- animation.removeListener(this);
- }
- });
- } else {
- outerOpacityAnim.addListener(mAnimationListener);
- }
- } else {
- outerOpacityAnim = ObjectAnimator.ofFloat(this, "outerOpacity", 0);
- outerOpacityAnim.setAutoCancel(true);
- outerOpacityAnim.setDuration(opacityDuration);
- outerOpacityAnim.addListener(mAnimationListener);
- }
+ opacityAnim.addListener(mAnimationListener);
mAnimRadius = radiusAnim;
mAnimOpacity = opacityAnim;
- mAnimOuterOpacity = outerOpacityAnim;
- mAnimX = opacityAnim;
- mAnimY = opacityAnim;
+ mAnimX = xAnim;
+ mAnimY = yAnim;
radiusAnim.start();
opacityAnim.start();
- outerOpacityAnim.start();
xAnim.start();
yAnim.start();
}
@@ -579,10 +456,6 @@
mAnimOpacity.cancel();
}
- if (mAnimOuterOpacity != null) {
- mAnimOuterOpacity.cancel();
- }
-
if (mAnimX != null) {
mAnimX.cancel();
}
@@ -597,7 +470,7 @@
*/
private void cancelHardwareAnimations() {
final ArrayList<RenderNodeAnimator> runningAnimations = mRunningAnimations;
- final int N = runningAnimations == null ? 0 : runningAnimations.size();
+ final int N = runningAnimations.size();
for (int i = 0; i < N; i++) {
runningAnimations.get(i).cancel();
}
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
new file mode 100644
index 0000000..d404ccd
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2013 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.drawable;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.util.MathUtils;
+import android.view.HardwareCanvas;
+import android.view.RenderNodeAnimator;
+import android.view.animation.LinearInterpolator;
+
+import java.util.ArrayList;
+
+/**
+ * Draws a Material ripple.
+ */
+class RippleBackground {
+ private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+ private static final TimeInterpolator DECEL_INTERPOLATOR = new LogInterpolator();
+
+ private static final float GLOBAL_SPEED = 1.0f;
+ private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024.0f * GLOBAL_SPEED;
+ private static final float WAVE_OPACITY_DECAY_VELOCITY = 3.0f / GLOBAL_SPEED;
+ private static final float WAVE_OUTER_OPACITY_VELOCITY_MAX = 4.5f * GLOBAL_SPEED;
+ private static final float WAVE_OUTER_OPACITY_VELOCITY_MIN = 1.5f * GLOBAL_SPEED;
+ private static final float WAVE_OUTER_SIZE_INFLUENCE_MAX = 200f;
+ private static final float WAVE_OUTER_SIZE_INFLUENCE_MIN = 40f;
+
+ private static final long RIPPLE_ENTER_DELAY = 80;
+
+ // Hardware animators.
+ private final ArrayList<RenderNodeAnimator> mRunningAnimations =
+ new ArrayList<RenderNodeAnimator>();
+ private final ArrayList<RenderNodeAnimator> mPendingAnimations =
+ new ArrayList<RenderNodeAnimator>();
+
+ private final RippleDrawable mOwner;
+
+ /** Bounds used for computing max radius. */
+ private final Rect mBounds;
+
+ /** Full-opacity color for drawing this ripple. */
+ private int mColor;
+
+ /** Maximum ripple radius. */
+ private float mOuterRadius;
+
+ /** Screen density used to adjust pixel-based velocities. */
+ private float mDensity;
+
+ private float mStartingX;
+ private float mStartingY;
+ private float mClampedStartingX;
+ private float mClampedStartingY;
+
+ // Hardware rendering properties.
+ private CanvasProperty<Paint> mPropOuterPaint;
+ private CanvasProperty<Float> mPropOuterRadius;
+ private CanvasProperty<Float> mPropOuterX;
+ private CanvasProperty<Float> mPropOuterY;
+
+ // Software animators.
+ private ObjectAnimator mAnimOuterOpacity;
+ private ObjectAnimator mAnimX;
+ private ObjectAnimator mAnimY;
+
+ // Temporary paint used for creating canvas properties.
+ private Paint mTempPaint;
+
+ // Software rendering properties.
+ private float mOuterOpacity = 0;
+ private float mOuterX;
+ private float mOuterY;
+
+ // Values used to tween between the start and end positions.
+ private float mTweenX = 0;
+ private float mTweenY = 0;
+
+ /** Whether we should be drawing hardware animations. */
+ private boolean mHardwareAnimating;
+
+ /** Whether we can use hardware acceleration for the exit animation. */
+ private boolean mCanUseHardware;
+
+ /** Whether we have an explicit maximum radius. */
+ private boolean mHasMaxRadius;
+
+ /**
+ * Creates a new ripple.
+ */
+ public RippleBackground(RippleDrawable owner, Rect bounds, float startingX, float startingY) {
+ mOwner = owner;
+ mBounds = bounds;
+
+ mStartingX = startingX;
+ mStartingY = startingY;
+ }
+
+ public void setup(int maxRadius, int color, float density) {
+ mColor = color | 0xFF000000;
+
+ if (maxRadius != RippleDrawable.RADIUS_AUTO) {
+ mHasMaxRadius = true;
+ mOuterRadius = maxRadius;
+ } else {
+ final float halfWidth = mBounds.width() / 2.0f;
+ final float halfHeight = mBounds.height() / 2.0f;
+ mOuterRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
+ }
+
+ mOuterX = 0;
+ mOuterY = 0;
+ mDensity = density;
+
+ clampStartingPosition();
+ }
+
+ private void clampStartingPosition() {
+ final float cX = mBounds.exactCenterX();
+ final float cY = mBounds.exactCenterY();
+ final float dX = mStartingX - cX;
+ final float dY = mStartingY - cY;
+ final float r = mOuterRadius;
+ if (dX * dX + dY * dY > r * r) {
+ // Point is outside the circle, clamp to the circumference.
+ final double angle = Math.atan2(dY, dX);
+ mClampedStartingX = cX + (float) (Math.cos(angle) * r);
+ mClampedStartingY = cY + (float) (Math.sin(angle) * r);
+ } else {
+ mClampedStartingX = mStartingX;
+ mClampedStartingY = mStartingY;
+ }
+ }
+
+ public void onHotspotBoundsChanged() {
+ if (!mHasMaxRadius) {
+ final float halfWidth = mBounds.width() / 2.0f;
+ final float halfHeight = mBounds.height() / 2.0f;
+ mOuterRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
+
+ clampStartingPosition();
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public void setOuterOpacity(float a) {
+ mOuterOpacity = a;
+ invalidateSelf();
+ }
+
+ @SuppressWarnings("unused")
+ public float getOuterOpacity() {
+ return mOuterOpacity;
+ }
+
+ @SuppressWarnings("unused")
+ public void setXGravity(float x) {
+ mTweenX = x;
+ invalidateSelf();
+ }
+
+ @SuppressWarnings("unused")
+ public float getXGravity() {
+ return mTweenX;
+ }
+
+ @SuppressWarnings("unused")
+ public void setYGravity(float y) {
+ mTweenY = y;
+ invalidateSelf();
+ }
+
+ @SuppressWarnings("unused")
+ public float getYGravity() {
+ return mTweenY;
+ }
+
+ /**
+ * Draws the ripple centered at (0,0) using the specified paint.
+ */
+ public boolean draw(Canvas c, Paint p) {
+ final boolean canUseHardware = c.isHardwareAccelerated();
+ if (mCanUseHardware != canUseHardware && mCanUseHardware) {
+ // We've switched from hardware to non-hardware mode. Panic.
+ cancelHardwareAnimations();
+ }
+ mCanUseHardware = canUseHardware;
+
+ final boolean hasContent;
+ if (canUseHardware && mHardwareAnimating) {
+ hasContent = drawHardware((HardwareCanvas) c);
+ } else {
+ hasContent = drawSoftware(c, p);
+ }
+
+ return hasContent;
+ }
+
+ private boolean drawHardware(HardwareCanvas c) {
+ // If we have any pending hardware animations, cancel any running
+ // animations and start those now.
+ final ArrayList<RenderNodeAnimator> pendingAnimations = mPendingAnimations;
+ final int N = pendingAnimations.size();
+ if (N > 0) {
+ cancelHardwareAnimations();
+
+ for (int i = 0; i < N; i++) {
+ pendingAnimations.get(i).setTarget(c);
+ pendingAnimations.get(i).start();
+ }
+
+ mRunningAnimations.addAll(pendingAnimations);
+ pendingAnimations.clear();
+ }
+
+ c.drawCircle(mPropOuterX, mPropOuterY, mPropOuterRadius, mPropOuterPaint);
+
+ return true;
+ }
+
+ private boolean drawSoftware(Canvas c, Paint p) {
+ boolean hasContent = false;
+
+ // Cache the paint alpha so we can restore it later.
+ final int paintAlpha = p.getAlpha();
+
+ final int outerAlpha = (int) (paintAlpha * mOuterOpacity + 0.5f);
+ if (outerAlpha > 0 && mOuterRadius > 0) {
+ p.setAlpha(outerAlpha);
+ p.setStyle(Style.FILL);
+ c.drawCircle(mOuterX, mOuterY, mOuterRadius, p);
+ hasContent = true;
+ }
+
+ p.setAlpha(paintAlpha);
+
+ return hasContent;
+ }
+
+ /**
+ * Returns the maximum bounds of the ripple relative to the ripple center.
+ */
+ public void getBounds(Rect bounds) {
+ final int outerX = (int) mOuterX;
+ final int outerY = (int) mOuterY;
+ final int r = (int) mOuterRadius;
+ bounds.set(outerX - r, outerY - r, outerX + r, outerY + r);
+ }
+
+ /**
+ * Specifies the starting position relative to the drawable bounds. No-op if
+ * the ripple has already entered.
+ */
+ public void move(float x, float y) {
+ mStartingX = x;
+ mStartingY = y;
+
+ clampStartingPosition();
+ }
+
+ /**
+ * Starts the enter animation.
+ */
+ public void enter() {
+ final int radiusDuration = (int)
+ (1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5);
+ final int outerDuration = (int) (1000 * 1.0f / WAVE_OUTER_OPACITY_VELOCITY_MIN);
+
+ final ObjectAnimator cX = ObjectAnimator.ofFloat(this, "xGravity", 1);
+ cX.setAutoCancel(true);
+ cX.setDuration(radiusDuration);
+ cX.setInterpolator(LINEAR_INTERPOLATOR);
+ cX.setStartDelay(RIPPLE_ENTER_DELAY);
+
+ final ObjectAnimator cY = ObjectAnimator.ofFloat(this, "yGravity", 1);
+ cY.setAutoCancel(true);
+ cY.setDuration(radiusDuration);
+ cY.setInterpolator(LINEAR_INTERPOLATOR);
+ cY.setStartDelay(RIPPLE_ENTER_DELAY);
+
+ final ObjectAnimator outer = ObjectAnimator.ofFloat(this, "outerOpacity", 0, 1);
+ outer.setAutoCancel(true);
+ outer.setDuration(outerDuration);
+ outer.setInterpolator(LINEAR_INTERPOLATOR);
+
+ mAnimOuterOpacity = outer;
+ mAnimX = cX;
+ mAnimY = cY;
+
+ // Enter animations always run on the UI thread, since it's unlikely
+ // that anything interesting is happening until the user lifts their
+ // finger.
+ outer.start();
+ cX.start();
+ cY.start();
+ }
+
+ /**
+ * Starts the exit animation.
+ */
+ public void exit() {
+ cancelSoftwareAnimations();
+
+ // Scale the outer max opacity and opacity velocity based
+ // on the size of the outer radius.
+ final int opacityDuration = (int) (1000 / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
+ final float outerSizeInfluence = MathUtils.constrain(
+ (mOuterRadius - WAVE_OUTER_SIZE_INFLUENCE_MIN * mDensity)
+ / (WAVE_OUTER_SIZE_INFLUENCE_MAX * mDensity), 0, 1);
+ final float outerOpacityVelocity = MathUtils.lerp(WAVE_OUTER_OPACITY_VELOCITY_MIN,
+ WAVE_OUTER_OPACITY_VELOCITY_MAX, outerSizeInfluence);
+
+ // Determine at what time the inner and outer opacity intersect.
+ // inner(t) = mOpacity - t * WAVE_OPACITY_DECAY_VELOCITY / 1000
+ // outer(t) = mOuterOpacity + t * WAVE_OUTER_OPACITY_VELOCITY / 1000
+ final int outerInflection = Math.max(0, (int) (1000 * (1 - mOuterOpacity)
+ / (WAVE_OPACITY_DECAY_VELOCITY + outerOpacityVelocity) + 0.5f));
+ final int inflectionOpacity = (int) (255 * (mOuterOpacity + outerInflection
+ * outerOpacityVelocity * outerSizeInfluence / 1000) + 0.5f);
+
+ if (mCanUseHardware) {
+ exitHardware(opacityDuration, outerInflection, inflectionOpacity);
+ } else {
+ exitSoftware(opacityDuration, outerInflection, inflectionOpacity);
+ }
+ }
+
+ private void exitHardware(int opacityDuration, int outerInflection, int inflectionOpacity) {
+ mPendingAnimations.clear();
+
+ // TODO: Adjust background by starting position.
+ final float startX = MathUtils.lerp(
+ mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
+ final float startY = MathUtils.lerp(
+ mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
+
+ final Paint outerPaint = getTempPaint();
+ outerPaint.setAntiAlias(true);
+ outerPaint.setColor(mColor);
+ outerPaint.setAlpha((int) (255 * mOuterOpacity + 0.5f));
+ outerPaint.setStyle(Style.FILL);
+ mPropOuterPaint = CanvasProperty.createPaint(outerPaint);
+ mPropOuterRadius = CanvasProperty.createFloat(mOuterRadius);
+ mPropOuterX = CanvasProperty.createFloat(mOuterX);
+ mPropOuterY = CanvasProperty.createFloat(mOuterY);
+
+ final RenderNodeAnimator outerOpacityAnim;
+ if (outerInflection > 0) {
+ // Outer opacity continues to increase for a bit.
+ outerOpacityAnim = new RenderNodeAnimator(
+ mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, inflectionOpacity);
+ outerOpacityAnim.setDuration(outerInflection);
+ outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
+
+ // Chain the outer opacity exit animation.
+ final int outerDuration = opacityDuration - outerInflection;
+ if (outerDuration > 0) {
+ final RenderNodeAnimator outerFadeOutAnim = new RenderNodeAnimator(
+ mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
+ outerFadeOutAnim.setDuration(outerDuration);
+ outerFadeOutAnim.setInterpolator(LINEAR_INTERPOLATOR);
+ outerFadeOutAnim.setStartDelay(outerInflection);
+ outerFadeOutAnim.setStartValue(inflectionOpacity);
+ outerFadeOutAnim.addListener(mAnimationListener);
+
+ mPendingAnimations.add(outerFadeOutAnim);
+ } else {
+ outerOpacityAnim.addListener(mAnimationListener);
+ }
+ } else {
+ outerOpacityAnim = new RenderNodeAnimator(
+ mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
+ outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
+ outerOpacityAnim.setDuration(opacityDuration);
+ outerOpacityAnim.addListener(mAnimationListener);
+ }
+
+ mPendingAnimations.add(outerOpacityAnim);
+
+ mHardwareAnimating = true;
+
+ invalidateSelf();
+ }
+
+ private Paint getTempPaint() {
+ if (mTempPaint == null) {
+ mTempPaint = new Paint();
+ }
+ return mTempPaint;
+ }
+
+ private void exitSoftware(int opacityDuration, int outerInflection, int inflectionOpacity) {
+ final ObjectAnimator xAnim = ObjectAnimator.ofFloat(this, "xGravity", 1);
+ xAnim.setAutoCancel(true);
+ xAnim.setDuration(opacityDuration);
+ xAnim.setInterpolator(DECEL_INTERPOLATOR);
+
+ final ObjectAnimator yAnim = ObjectAnimator.ofFloat(this, "yGravity", 1);
+ yAnim.setAutoCancel(true);
+ yAnim.setDuration(opacityDuration);
+ yAnim.setInterpolator(DECEL_INTERPOLATOR);
+
+ final ObjectAnimator outerOpacityAnim;
+ if (outerInflection > 0) {
+ // Outer opacity continues to increase for a bit.
+ outerOpacityAnim = ObjectAnimator.ofFloat(this,
+ "outerOpacity", inflectionOpacity / 255.0f);
+ outerOpacityAnim.setAutoCancel(true);
+ outerOpacityAnim.setDuration(outerInflection);
+ outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
+
+ // Chain the outer opacity exit animation.
+ final int outerDuration = opacityDuration - outerInflection;
+ if (outerDuration > 0) {
+ outerOpacityAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ final ObjectAnimator outerFadeOutAnim = ObjectAnimator.ofFloat(
+ RippleBackground.this, "outerOpacity", 0);
+ outerFadeOutAnim.setAutoCancel(true);
+ outerFadeOutAnim.setDuration(outerDuration);
+ outerFadeOutAnim.setInterpolator(LINEAR_INTERPOLATOR);
+ outerFadeOutAnim.addListener(mAnimationListener);
+
+ mAnimOuterOpacity = outerFadeOutAnim;
+
+ outerFadeOutAnim.start();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ animation.removeListener(this);
+ }
+ });
+ } else {
+ outerOpacityAnim.addListener(mAnimationListener);
+ }
+ } else {
+ outerOpacityAnim = ObjectAnimator.ofFloat(this, "outerOpacity", 0);
+ outerOpacityAnim.setAutoCancel(true);
+ outerOpacityAnim.setDuration(opacityDuration);
+ outerOpacityAnim.addListener(mAnimationListener);
+ }
+
+ mAnimOuterOpacity = outerOpacityAnim;
+ mAnimX = xAnim;
+ mAnimY = yAnim;
+
+ outerOpacityAnim.start();
+ xAnim.start();
+ yAnim.start();
+ }
+
+ /**
+ * Cancel all animations.
+ */
+ public void cancel() {
+ cancelSoftwareAnimations();
+ cancelHardwareAnimations();
+ }
+
+ private void cancelSoftwareAnimations() {
+ if (mAnimOuterOpacity != null) {
+ mAnimOuterOpacity.cancel();
+ }
+
+ if (mAnimX != null) {
+ mAnimX.cancel();
+ }
+
+ if (mAnimY != null) {
+ mAnimY.cancel();
+ }
+ }
+
+ /**
+ * Cancels any running hardware animations.
+ */
+ private void cancelHardwareAnimations() {
+ final ArrayList<RenderNodeAnimator> runningAnimations = mRunningAnimations;
+ final int N = runningAnimations.size();
+ for (int i = 0; i < N; i++) {
+ runningAnimations.get(i).cancel();
+ }
+
+ runningAnimations.clear();
+ }
+
+ private void removeSelf() {
+ // The owner will invalidate itself.
+ mOwner.removeBackground(this);
+ }
+
+ private void invalidateSelf() {
+ mOwner.invalidateSelf();
+ }
+
+ private final AnimatorListenerAdapter mAnimationListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ removeSelf();
+ }
+ };
+
+ /**
+ * Interpolator with a smooth log deceleration
+ */
+ private static final class LogInterpolator implements TimeInterpolator {
+ @Override
+ public float getInterpolation(float input) {
+ return 1 - (float) Math.pow(400, -input * 1.4);
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 4a1d640..0c9c558 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -120,8 +120,22 @@
/** The masking layer, e.g. the layer with id R.id.mask. */
private Drawable mMask;
- /** The current hotspot. May be actively animating or pending entry. */
- private Ripple mHotspot;
+ /** The current background. May be actively animating or pending entry. */
+ private RippleBackground mBackground;
+
+ /** Whether we expect to draw a background when visible. */
+ private boolean mBackgroundActive;
+
+ /** The current ripple. May be actively animating or pending entry. */
+ private Ripple mRipple;
+
+ /** Whether we expect to draw a ripple when visible. */
+ private boolean mRippleActive;
+
+ // Hotspot coordinates that are awaiting activation.
+ private float mPendingX;
+ private float mPendingY;
+ private boolean mHasPending;
/**
* Lazily-created array of actively animating ripples. Inactive ripples are
@@ -142,9 +156,6 @@
/** Whether bounds are being overridden. */
private boolean mOverrideBounds;
- /** Whether the hotspot is currently active (e.g. focused or pressed). */
- private boolean mActive;
-
/**
* Constructor used for drawable inflation.
*/
@@ -178,6 +189,7 @@
setColor(color);
ensurePadding();
+ initializeFromState();
}
@Override
@@ -204,20 +216,26 @@
protected boolean onStateChange(int[] stateSet) {
super.onStateChange(stateSet);
- // TODO: This would make more sense in a StateListDrawable.
- boolean active = false;
boolean enabled = false;
+ boolean pressed = false;
+ boolean focused = false;
+
final int N = stateSet.length;
for (int i = 0; i < N; i++) {
if (stateSet[i] == R.attr.state_enabled) {
enabled = true;
}
if (stateSet[i] == R.attr.state_focused
- || stateSet[i] == R.attr.state_pressed) {
- active = true;
+ || stateSet[i] == R.attr.state_selected) {
+ focused = true;
+ }
+ if (stateSet[i] == R.attr.state_pressed) {
+ pressed = true;
}
}
- setActive(active && enabled);
+
+ setRippleActive(enabled && pressed);
+ setBackgroundActive(focused || (enabled && pressed));
// Update the paint color. Only applicable when animated in software.
if (mRipplePaint != null && mState.mColor != null) {
@@ -234,14 +252,24 @@
return false;
}
- private void setActive(boolean active) {
- if (mActive != active) {
- mActive = active;
-
+ private void setRippleActive(boolean active) {
+ if (mRippleActive != active) {
+ mRippleActive = active;
if (active) {
- activateHotspot();
+ activateRipple();
} else {
- removeHotspot();
+ removeRipple();
+ }
+ }
+ }
+
+ private void setBackgroundActive(boolean active) {
+ if (mBackgroundActive != active) {
+ mBackgroundActive = active;
+ if (active) {
+ activateBackground();
+ } else {
+ removeBackground();
}
}
}
@@ -260,11 +288,23 @@
@Override
public boolean setVisible(boolean visible, boolean restart) {
+ final boolean changed = super.setVisible(visible, restart);
+
if (!visible) {
clearHotspots();
+ } else if (changed) {
+ // If we just became visible, ensure the background and ripple
+ // visibilities are consistent with their internal states.
+ if (mRippleActive) {
+ activateRipple();
+ }
+
+ if (mBackgroundActive) {
+ activateBackground();
+ }
}
- return super.setVisible(visible, restart);
+ return changed;
}
/**
@@ -398,55 +438,101 @@
@Override
public void setHotspot(float x, float y) {
- if (mHotspot == null) {
- mHotspot = new Ripple(this, mHotspotBounds, x, y);
+ if (mRipple == null || mBackground == null) {
+ mPendingX = x;
+ mPendingY = y;
+ mHasPending = true;
+ }
- if (mActive) {
- activateHotspot();
- }
- } else {
- mHotspot.move(x, y);
+ if (mRipple != null) {
+ mRipple.move(x, y);
+ }
+
+ if (mBackground != null) {
+ mBackground.move(x, y);
}
}
/**
* Creates an active hotspot at the specified location.
*/
- private void activateHotspot() {
- if (mAnimatingRipplesCount >= MAX_RIPPLES) {
- // This should never happen unless the user is tapping like a maniac
- // or there is a bug that's preventing ripples from being removed.
- Log.d(LOG_TAG, "Max ripple count exceeded", new RuntimeException());
- return;
- }
-
- if (mHotspot == null) {
- final float x = mHotspotBounds.exactCenterX();
- final float y = mHotspotBounds.exactCenterY();
- mHotspot = new Ripple(this, mHotspotBounds, x, y);
+ private void activateBackground() {
+ if (mBackground == null) {
+ final float x;
+ final float y;
+ if (mHasPending) {
+ mHasPending = false;
+ x = mPendingX;
+ y = mPendingY;
+ } else {
+ x = mHotspotBounds.exactCenterX();
+ y = mHotspotBounds.exactCenterY();
+ }
+ mBackground = new RippleBackground(this, mHotspotBounds, x, y);
}
final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
- mHotspot.setup(mState.mMaxRadius, color, mDensity);
- mHotspot.enter();
+ mBackground.setup(mState.mMaxRadius, color, mDensity);
+ mBackground.enter();
+ }
+
+ private void removeBackground() {
+ if (mBackground != null) {
+ // Don't null out the background, we need it to draw!
+ mBackground.exit();
+ }
+ }
+
+ /**
+ * Creates an active hotspot at the specified location.
+ */
+ private void activateRipple() {
+ if (mAnimatingRipplesCount >= MAX_RIPPLES) {
+ // This should never happen unless the user is tapping like a maniac
+ // or there is a bug that's preventing ripples from being removed.
+ return;
+ }
+
+ if (mRipple == null) {
+ final float x;
+ final float y;
+ if (mHasPending) {
+ mHasPending = false;
+ x = mPendingX;
+ y = mPendingY;
+ } else {
+ x = mHotspotBounds.exactCenterX();
+ y = mHotspotBounds.exactCenterY();
+ }
+ mRipple = new Ripple(this, mHotspotBounds, x, y);
+ }
+
+ final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
+ mRipple.setup(mState.mMaxRadius, color, mDensity);
+ mRipple.enter();
if (mAnimatingRipples == null) {
mAnimatingRipples = new Ripple[MAX_RIPPLES];
}
- mAnimatingRipples[mAnimatingRipplesCount++] = mHotspot;
+ mAnimatingRipples[mAnimatingRipplesCount++] = mRipple;
}
- private void removeHotspot() {
- if (mHotspot != null) {
- mHotspot.exit();
- mHotspot = null;
+ private void removeRipple() {
+ if (mRipple != null) {
+ mRipple.exit();
+ mRipple = null;
}
}
private void clearHotspots() {
- if (mHotspot != null) {
- mHotspot.cancel();
- mHotspot = null;
+ if (mRipple != null) {
+ mRipple.cancel();
+ mRipple = null;
+ }
+
+ if (mBackground != null) {
+ mBackground.cancel();
+ mBackground = null;
}
final int count = mAnimatingRipplesCount;
@@ -486,6 +572,10 @@
for (int i = 0; i < count; i++) {
ripples[i].onHotspotBoundsChanged();
}
+
+ if (mBackground != null) {
+ mBackground.onHotspotBoundsChanged();
+ }
}
/**
@@ -524,18 +614,28 @@
// Next, try to draw the ripples (into a layer if necessary). If we need
// to mask against the underlying content, set the xfermode to SRC_ATOP.
final PorterDuffXfermode xfermode = (hasMask || !drawNonMaskContent) ? SRC_OVER : SRC_ATOP;
- final int rippleLayer = drawRippleLayer(canvas, bounds, xfermode);
+
+ // If we have a background and a non-opaque mask, draw the masking layer.
+ final int backgroundLayer = drawBackgroundLayer(canvas, bounds, xfermode);
+ if (backgroundLayer >= 0) {
+ if (drawMask) {
+ drawMaskingLayer(canvas, bounds, DST_IN);
+ }
+ canvas.restoreToCount(backgroundLayer);
+ }
// If we have ripples and a non-opaque mask, draw the masking layer.
- if (rippleLayer >= 0 && drawMask) {
- drawMaskingLayer(canvas, bounds, DST_IN);
+ final int rippleLayer = drawRippleLayer(canvas, bounds, xfermode);
+ if (rippleLayer >= 0) {
+ if (drawMask) {
+ drawMaskingLayer(canvas, bounds, DST_IN);
+ }
+ canvas.restoreToCount(rippleLayer);
}
// Composite the layers if needed.
if (contentLayer >= 0) {
canvas.restoreToCount(contentLayer);
- } else if (rippleLayer >= 0) {
- canvas.restoreToCount(rippleLayer);
}
}
@@ -550,15 +650,20 @@
final int count = mAnimatingRipplesCount;
final int index = getRippleIndex(ripple);
if (index >= 0) {
- for (int i = index + 1; i < count; i++) {
- ripples[i - 1] = ripples[i];
- }
+ System.arraycopy(ripples, index + 1, ripples, index + 1 - 1, count - (index + 1));
ripples[count - 1] = null;
mAnimatingRipplesCount--;
invalidateSelf();
}
}
+ void removeBackground(RippleBackground background) {
+ if (mBackground == background) {
+ mBackground = null;
+ invalidateSelf();
+ }
+ }
+
private int getRippleIndex(Ripple ripple) {
final Ripple[] ripples = mAnimatingRipples;
final int count = mAnimatingRipplesCount;
@@ -577,7 +682,7 @@
// We don't need a layer if we don't expect to draw any ripples, we have
// an explicit mask, or if the non-mask content is all opaque.
boolean needsLayer = false;
- if (mAnimatingRipplesCount > 0 && mMask == null) {
+ if ((mAnimatingRipplesCount > 0 || mBackground != null) && mMask == null) {
for (int i = 0; i < count; i++) {
if (array[i].mId != R.id.mask
&& array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) {
@@ -601,12 +706,62 @@
return restoreToCount;
}
- private int drawRippleLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
- final int count = mAnimatingRipplesCount;
- if (count == 0) {
- return -1;
+ private int drawBackgroundLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
+ // Separate the ripple color and alpha channel. The alpha will be
+ // applied when we merge the ripples down to the canvas.
+ final int rippleARGB;
+ if (mState.mColor != null) {
+ rippleARGB = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
+ } else {
+ rippleARGB = Color.TRANSPARENT;
}
+ if (mRipplePaint == null) {
+ mRipplePaint = new Paint();
+ mRipplePaint.setAntiAlias(true);
+ }
+
+ final int rippleAlpha = Color.alpha(rippleARGB);
+ final Paint ripplePaint = mRipplePaint;
+ ripplePaint.setColor(rippleARGB);
+ ripplePaint.setAlpha(0xFF);
+
+ boolean drewRipples = false;
+ int restoreToCount = -1;
+ int restoreTranslate = -1;
+
+ // Draw background.
+ final RippleBackground background = mBackground;
+ if (background != null) {
+ // If we're masking the ripple layer, make sure we have a layer
+ // first. This will merge SRC_OVER (directly) onto the canvas.
+ final Paint maskingPaint = getMaskingPaint(mode);
+ maskingPaint.setAlpha(rippleAlpha);
+ restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
+ bounds.right, bounds.bottom, maskingPaint);
+
+ restoreTranslate = canvas.save();
+ // Translate the canvas to the current hotspot bounds.
+ canvas.translate(mHotspotBounds.exactCenterX(), mHotspotBounds.exactCenterY());
+
+ drewRipples = background.draw(canvas, ripplePaint);
+ }
+
+ // Always restore the translation.
+ if (restoreTranslate >= 0) {
+ canvas.restoreToCount(restoreTranslate);
+ }
+
+ // If we created a layer with no content, merge it immediately.
+ if (restoreToCount >= 0 && !drewRipples) {
+ canvas.restoreToCount(restoreToCount);
+ restoreToCount = -1;
+ }
+
+ return restoreToCount;
+ }
+
+ private int drawRippleLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
// Separate the ripple color and alpha channel. The alpha will be
// applied when we merge the ripples down to the canvas.
final int rippleARGB;
@@ -631,6 +786,7 @@
int restoreTranslate = -1;
// Draw ripples and update the animating ripples array.
+ final int count = mAnimatingRipplesCount;
final Ripple[] ripples = mAnimatingRipples;
for (int i = 0; i < count; i++) {
final Ripple ripple = ripples[i];
@@ -705,6 +861,13 @@
drawingBounds.union(rippleBounds);
}
+ final RippleBackground background = mBackground;
+ if (background != null) {
+ background.getBounds(rippleBounds);
+ rippleBounds.offset(cX, cY);
+ drawingBounds.union(rippleBounds);
+ }
+
dirtyBounds.union(drawingBounds);
dirtyBounds.union(super.getDirtyBounds());
return dirtyBounds;
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 13ef89b..8ed6776d 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -740,9 +740,7 @@
final float minScale = Math.min(scaleX, scaleY);
mFinalPathMatrix.set(vGroup.mStackedMatrix);
- mFinalPathMatrix.postScale(scaleX, scaleY, mViewportWidth / 2f, mViewportHeight / 2f);
- mFinalPathMatrix.postTranslate(
- w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f);
+ mFinalPathMatrix.postScale(scaleX, scaleY);
vPath.toPath(mPath);
final Path path = mPath;
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index 937b7c6..181230a 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -48,7 +48,6 @@
const Vector3* vertices, int vertexCount, const Vector3& centroid3d,
float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) {
const int rays = SHADOW_RAY_COUNT;
- VertexBuffer::Mode mode = VertexBuffer::kOnePolyRingShadow;
// Validate the inputs.
if (vertexCount < 3 || heightFactor <= 0 || rays <= 0
|| geomFactor <= 0) {
@@ -98,9 +97,9 @@
// calculate the normal N, which should be perpendicular to the edge of the
// polygon (represented by the neighbor intersection points) .
// Shadow's vertices will be generated as : P + N * scale.
- const Vector2 centroid2d = Vector2(centroid3d.x, centroid3d.y);
+ const Vector2 centroid2d = {centroid3d.x, centroid3d.y};
for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
- Vector2 normal(1.0f, 0.0f);
+ Vector2 normal = {1.0f, 0.0f};
calculateNormal(rays, rayIndex, dir.array(), rayDist, normal);
// The vertex should be start from rayDist[i] then scale the
@@ -124,18 +123,23 @@
opacity);
}
- // If caster isn't opaque, we need to to fill the umbra by storing the umbra's
- // centroid in the innermost ring of vertices.
- if (!isCasterOpaque) {
- mode = VertexBuffer::kTwoPolyRingShadow;
+ if (isCasterOpaque) {
+ // skip inner ring, calc bounds over filled portion of buffer
+ shadowVertexBuffer.computeBounds<AlphaVertex>(2 * rays);
+ shadowVertexBuffer.setMode(VertexBuffer::kOnePolyRingShadow);
+ } else {
+ // If caster isn't opaque, we need to to fill the umbra by storing the umbra's
+ // centroid in the innermost ring of vertices.
float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor);
AlphaVertex centroidXYA;
AlphaVertex::set(¢roidXYA, centroid2d.x, centroid2d.y, centroidAlpha);
for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
shadowVertices[2 * rays + rayIndex] = centroidXYA;
}
+ // calc bounds over entire buffer
+ shadowVertexBuffer.computeBounds<AlphaVertex>();
+ shadowVertexBuffer.setMode(VertexBuffer::kTwoPolyRingShadow);
}
- shadowVertexBuffer.setMode(mode);
#if DEBUG_SHADOW
for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) {
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index 9202e49..bf7828c 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -49,7 +49,7 @@
LayerRenderer(RenderState& renderState, Layer* layer);
virtual ~LayerRenderer();
- virtual void onViewportInitialized(int width, int height) { /* do nothing */ }
+ virtual void onViewportInitialized() { /* do nothing */ }
virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque);
virtual status_t clear(float left, float top, float right, float bottom, bool opaque);
virtual void finish();
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 5a96132..64df6c8 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -132,19 +132,21 @@
///////////////////////////////////////////////////////////////////////////////
OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
- : mCaches(Caches::getInstance())
+ : mFrameStarted(false)
+ , mCaches(Caches::getInstance())
, mExtensions(Extensions::getInstance())
- , mRenderState(renderState) {
+ , mRenderState(renderState)
+ , mScissorOptimizationDisabled(false)
+ , mCountOverdraw(false)
+ , mLightCenter((Vector3){FLT_MIN, FLT_MIN, FLT_MIN})
+ , mLightRadius(FLT_MIN)
+ , mAmbientShadowAlpha(0)
+ , mSpotShadowAlpha(0) {
// *set* draw modifiers to be 0
memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
mDrawModifiers.mOverrideLayerAlpha = 1.0f;
memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
-
- mFrameStarted = false;
- mCountOverdraw = false;
-
- mScissorOptimizationDisabled = false;
}
OpenGLRenderer::~OpenGLRenderer() {
@@ -163,6 +165,14 @@
}
}
+void OpenGLRenderer::initLight(const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
+ mLightCenter = lightCenter;
+ mLightRadius = lightRadius;
+ mAmbientShadowAlpha = ambientShadowAlpha;
+ mSpotShadowAlpha = spotShadowAlpha;
+}
+
///////////////////////////////////////////////////////////////////////////////
// Setup
///////////////////////////////////////////////////////////////////////////////
@@ -3172,13 +3182,13 @@
SkPaint paint;
paint.setAntiAlias(true); // want to use AlphaVertex
- if (ambientShadowVertexBuffer && mCaches.propertyAmbientShadowStrength > 0) {
- paint.setARGB(casterAlpha * mCaches.propertyAmbientShadowStrength, 0, 0, 0);
+ if (ambientShadowVertexBuffer && mAmbientShadowAlpha > 0) {
+ paint.setARGB(casterAlpha * mAmbientShadowAlpha, 0, 0, 0);
drawVertexBuffer(*ambientShadowVertexBuffer, &paint);
}
- if (spotShadowVertexBuffer && mCaches.propertySpotShadowStrength > 0) {
- paint.setARGB(casterAlpha * mCaches.propertySpotShadowStrength, 0, 0, 0);
+ if (spotShadowVertexBuffer && mSpotShadowAlpha > 0) {
+ paint.setARGB(casterAlpha * mSpotShadowAlpha, 0, 0, 0);
drawVertexBuffer(*spotShadowVertexBuffer, &paint);
}
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 4e7844b..f698b45 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -124,6 +124,8 @@
virtual ~OpenGLRenderer();
void initProperties();
+ void initLight(const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
virtual void onViewportInitialized();
virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque);
@@ -1010,6 +1012,12 @@
bool mSkipOutlineClip;
+ // Lighting + shadows
+ Vector3 mLightCenter;
+ float mLightRadius;
+ uint8_t mAmbientShadowAlpha;
+ uint8_t mSpotShadowAlpha;
+
friend class Layer;
friend class TextSetupFunctor;
friend class DrawBitmapOp;
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
index 024bdfd..83426e8 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -49,15 +49,20 @@
mBounds.set(outline->getBounds());
}
- bool isEmpty() const {
- return mType == kOutlineType_None;
+ void setEmpty() {
+ mType = kOutlineType_Empty;
+ mPath.reset();
}
- void setEmpty() {
+ void setNone() {
mType = kOutlineType_None;
mPath.reset();
}
+ bool isEmpty() const {
+ return mType == kOutlineType_Empty;
+ }
+
void setShouldClip(bool clip) {
mShouldClip = clip;
}
@@ -81,7 +86,7 @@
}
const SkPath* getPath() const {
- if (mType == kOutlineType_None) return NULL;
+ if (mType == kOutlineType_None || mType == kOutlineType_Empty) return NULL;
return &mPath;
}
@@ -89,8 +94,9 @@
private:
enum OutlineType {
kOutlineType_None = 0,
- kOutlineType_ConvexPath = 1,
- kOutlineType_RoundRect = 2
+ kOutlineType_Empty = 1,
+ kOutlineType_ConvexPath = 2,
+ kOutlineType_RoundRect = 3
};
bool mShouldClip;
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 310b107..209341c 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -90,7 +90,7 @@
*
* NOTE: assumes angles between normals 90 degrees or less
*/
-inline static vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
+inline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vector2& normalB) {
return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
}
@@ -129,7 +129,7 @@
float halfStrokeWidth;
float maxAlpha;
- inline void scaleOffsetForStrokeWidth(vec2& offset) const {
+ inline void scaleOffsetForStrokeWidth(Vector2& offset) const {
if (halfStrokeWidth == 0.0f) {
// hairline - compensate for scale
offset.x *= 0.5f * inverseScaleX;
@@ -143,9 +143,8 @@
* NOTE: the input will not always be a normal, especially for sharp edges - it should be the
* result of totalOffsetFromNormals (see documentation there)
*/
- inline vec2 deriveAAOffset(const vec2& offset) const {
- return vec2(offset.x * 0.5f * inverseScaleX,
- offset.y * 0.5f * inverseScaleY);
+ inline Vector2 deriveAAOffset(const Vector2& offset) const {
+ return (Vector2){offset.x * 0.5f * inverseScaleX, offset.y * 0.5f * inverseScaleY};
}
/**
@@ -208,16 +207,14 @@
int currentIndex = 0;
const Vertex* last = &(perimeter[perimeter.size() - 1]);
const Vertex* current = &(perimeter[0]);
- vec2 lastNormal(current->y - last->y,
- last->x - current->x);
+ Vector2 lastNormal = {current->y - last->y, last->x - current->x};
lastNormal.normalize();
for (unsigned int i = 0; i < perimeter.size(); i++) {
const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
- vec2 nextNormal(next->y - current->y,
- current->x - next->x);
+ Vector2 nextNormal = {next->y - current->y, current->x - next->x};
nextNormal.normalize();
- vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
paintInfo.scaleOffsetForStrokeWidth(totalOffset);
Vertex::set(&buffer[currentIndex++],
@@ -241,13 +238,14 @@
}
static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
- const vec2& normal, Vertex* buffer, int& currentIndex, bool begin) {
- vec2 strokeOffset = normal;
+ const Vector2& normal, Vertex* buffer, int& currentIndex, bool begin) {
+ Vector2 strokeOffset = normal;
paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
- vec2 referencePoint(center.x, center.y);
+ Vector2 referencePoint = {center.x, center.y};
if (paintInfo.cap == SkPaint::kSquare_Cap) {
- referencePoint += vec2(-strokeOffset.y, strokeOffset.x) * (begin ? -1 : 1);
+ Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
+ referencePoint += rotated * (begin ? -1 : 1);
}
Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
@@ -288,14 +286,14 @@
}
beginTheta += dTheta;
- vec2 beginRadialOffset(cos(beginTheta), sin(beginTheta));
+ Vector2 beginRadialOffset = {cos(beginTheta), sin(beginTheta)};
paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
Vertex::set(&buffer[capOffset],
vertices[0].x + beginRadialOffset.x,
vertices[0].y + beginRadialOffset.y);
endTheta += dTheta;
- vec2 endRadialOffset(cos(endTheta), sin(endTheta));
+ Vector2 endRadialOffset = {cos(endTheta), sin(endTheta)};
paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
Vertex::set(&buffer[allocSize - 1 - capOffset],
vertices[lastIndex].x + endRadialOffset.x,
@@ -306,22 +304,20 @@
int currentIndex = extra;
const Vertex* last = &(vertices[0]);
const Vertex* current = &(vertices[1]);
- vec2 lastNormal(current->y - last->y,
- last->x - current->x);
+ Vector2 lastNormal = {current->y - last->y, last->x - current->x};
lastNormal.normalize();
storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
for (unsigned int i = 1; i < vertices.size() - 1; i++) {
const Vertex* next = &(vertices[i + 1]);
- vec2 nextNormal(next->y - current->y,
- current->x - next->x);
+ Vector2 nextNormal = {next->y - current->y, current->x - next->x};
nextNormal.normalize();
- vec2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
- vec2 center(current->x, current->y);
+ Vector2 center = {current->x, current->y};
Vertex::set(&buffer[currentIndex++], center + strokeOffset);
Vertex::set(&buffer[currentIndex++], center - strokeOffset);
@@ -353,18 +349,16 @@
int currentIndex = 0;
const Vertex* last = &(perimeter[perimeter.size() - 1]);
const Vertex* current = &(perimeter[0]);
- vec2 lastNormal(current->y - last->y,
- last->x - current->x);
+ Vector2 lastNormal = {current->y - last->y, last->x - current->x};
lastNormal.normalize();
for (unsigned int i = 0; i < perimeter.size(); i++) {
const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
- vec2 nextNormal(next->y - current->y,
- current->x - next->x);
+ Vector2 nextNormal = {next->y - current->y, current->x - next->x};
nextNormal.normalize();
// AA point offset from original point is that point's normal, such that each side is offset
// by .5 pixels
- vec2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
+ Vector2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
AlphaVertex::set(&buffer[currentIndex++],
current->x + totalOffset.x,
@@ -407,7 +401,7 @@
* getStrokeVerticesFromUnclosedVerticesAA() below.
*/
inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
- AlphaVertex* buffer, bool isFirst, vec2 normal, int offset) {
+ AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) {
const int extra = paintInfo.capExtraDivisions();
const int extraOffset = (extra + 1) / 2;
const int capIndex = isFirst
@@ -416,27 +410,28 @@
if (isFirst) normal *= -1;
// TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
- vec2 AAOffset = paintInfo.deriveAAOffset(normal);
+ Vector2 AAOffset = paintInfo.deriveAAOffset(normal);
- vec2 strokeOffset = normal;
+ Vector2 strokeOffset = normal;
paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
- vec2 outerOffset = strokeOffset + AAOffset;
- vec2 innerOffset = strokeOffset - AAOffset;
+ Vector2 outerOffset = strokeOffset + AAOffset;
+ Vector2 innerOffset = strokeOffset - AAOffset;
- vec2 capAAOffset;
+ Vector2 capAAOffset = {0, 0};
if (paintInfo.cap != SkPaint::kRound_Cap) {
// if the cap is square or butt, the inside primary cap vertices will be inset in two
// directions - both normal to the stroke, and parallel to it.
- capAAOffset = vec2(-AAOffset.y, AAOffset.x);
+ capAAOffset = (Vector2){-AAOffset.y, AAOffset.x};
}
// determine referencePoint, the center point for the 4 primary cap vertices
const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1);
- vec2 referencePoint(point->x, point->y);
+ Vector2 referencePoint = {point->x, point->y};
if (paintInfo.cap == SkPaint::kSquare_Cap) {
// To account for square cap, move the primary cap vertices (that create the AA edge) by the
// stroke offset vector (rotated to be parallel to the stroke)
- referencePoint += vec2(-strokeOffset.y, strokeOffset.x);
+ Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
+ referencePoint += rotated;
}
AlphaVertex::set(&buffer[capIndex + 0],
@@ -469,7 +464,7 @@
for (int i = 0; i < extra; i++) {
theta += dTheta;
- vec2 radialOffset(cos(theta), sin(theta));
+ Vector2 radialOffset = {cos(theta), sin(theta)};
// scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
radialOffset *= radialScale;
@@ -592,8 +587,7 @@
const Vertex* last = &(vertices[0]);
const Vertex* current = &(vertices[1]);
- vec2 lastNormal(current->y - last->y,
- last->x - current->x);
+ Vector2 lastNormal = {current->y - last->y, last->x - current->x};
lastNormal.normalize();
// TODO: use normal from bezier traversal for cap, instead of from vertices
@@ -601,16 +595,15 @@
for (unsigned int i = 1; i < vertices.size() - 1; i++) {
const Vertex* next = &(vertices[i + 1]);
- vec2 nextNormal(next->y - current->y,
- current->x - next->x);
+ Vector2 nextNormal = {next->y - current->y, current->x - next->x};
nextNormal.normalize();
- vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
- vec2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
+ Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
- vec2 innerOffset = totalOffset;
+ Vector2 innerOffset = totalOffset;
paintInfo.scaleOffsetForStrokeWidth(innerOffset);
- vec2 outerOffset = innerOffset + AAOffset;
+ Vector2 outerOffset = innerOffset + AAOffset;
innerOffset -= AAOffset;
AlphaVertex::set(&buffer[currentAAOuterIndex++],
@@ -662,21 +655,19 @@
const Vertex* last = &(perimeter[perimeter.size() - 1]);
const Vertex* current = &(perimeter[0]);
- vec2 lastNormal(current->y - last->y,
- last->x - current->x);
+ Vector2 lastNormal = {current->y - last->y, last->x - current->x};
lastNormal.normalize();
for (unsigned int i = 0; i < perimeter.size(); i++) {
const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
- vec2 nextNormal(next->y - current->y,
- current->x - next->x);
+ Vector2 nextNormal = {next->y - current->y, current->x - next->x};
nextNormal.normalize();
- vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
- vec2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
+ Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
- vec2 innerOffset = totalOffset;
+ Vector2 innerOffset = totalOffset;
paintInfo.scaleOffsetForStrokeWidth(innerOffset);
- vec2 outerOffset = innerOffset + AAOffset;
+ Vector2 outerOffset = innerOffset + AAOffset;
innerOffset -= AAOffset;
AlphaVertex::set(&buffer[currentAAOuterIndex++],
@@ -794,16 +785,6 @@
vertexBuffer.setBounds(bounds);
}
-static void expandRectToCoverVertex(Rect& rect, float x, float y) {
- rect.left = fminf(rect.left, x);
- rect.top = fminf(rect.top, y);
- rect.right = fmaxf(rect.right, x);
- rect.bottom = fmaxf(rect.bottom, y);
-}
-static void expandRectToCoverVertex(Rect& rect, const Vertex& vertex) {
- expandRectToCoverVertex(rect, vertex.x, vertex.y);
-}
-
template <class TYPE>
static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
const float* points, int count, Rect& bounds) {
@@ -814,7 +795,7 @@
dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
for (int i = 0; i < count; i += 2) {
- expandRectToCoverVertex(bounds, points[i + 0], points[i + 1]);
+ bounds.expandToCoverVertex(points[i + 0], points[i + 1]);
dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
}
dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
@@ -896,8 +877,8 @@
}
// calculate bounds
- expandRectToCoverVertex(bounds, tempVerticesData[0]);
- expandRectToCoverVertex(bounds, tempVerticesData[1]);
+ bounds.expandToCoverVertex(tempVerticesData[0].x, tempVerticesData[0].y);
+ bounds.expandToCoverVertex(tempVerticesData[1].x, tempVerticesData[1].y);
}
// since multiple objects tessellated into buffer, separate them with degen tris
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 846ebdc..9311f99 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -241,6 +241,13 @@
bottom = ceilf(bottom);
}
+ void expandToCoverVertex(float x, float y) {
+ left = fminf(left, x);
+ top = fminf(top, y);
+ right = fmaxf(right, x);
+ bottom = fmaxf(bottom, y);
+ }
+
void dump(const char* label = NULL) const {
ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom);
}
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index fe03806..b77b36f 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -581,7 +581,7 @@
template <class T>
void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
- if (properties().getAlpha() <= 0.0f || properties().getOutline().isEmpty()) return;
+ if (properties().getAlpha() <= 0.0f || !properties().getOutline().getPath()) return;
mat4 shadowMatrixXY(transformFromParent);
applyViewPropertyTransforms(shadowMatrixXY);
@@ -776,16 +776,23 @@
*/
template <class T>
void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
+ const int level = handler.level();
+ if (mDisplayListData->isEmpty()) {
+ DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
+ return;
+ }
+
const bool drawLayer = (mLayer && (&renderer != mLayer->renderer));
// If we are updating the contents of mLayer, we don't want to apply any of
// the RenderNode's properties to this issueOperations pass. Those will all
// be applied when the layer is drawn, aka when this is true.
const bool useViewProperties = (!mLayer || drawLayer);
-
- const int level = handler.level();
- if (mDisplayListData->isEmpty() || (useViewProperties && properties().getAlpha() <= 0)) {
- DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
- return;
+ if (useViewProperties) {
+ const Outline& outline = properties().getOutline();
+ if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty())) {
+ DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", level * 2, "", this, getName());
+ return;
+ }
}
handler.startMark(getName());
@@ -829,7 +836,8 @@
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
const int saveCountOffset = renderer.getSaveCount() - 1;
const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
- for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+ const int size = static_cast<int>(mDisplayListData->displayListOps.size());
+ for (int i = 0; i < size; i++) {
DisplayListOp *op = mDisplayListData->displayListOps[i];
#if DEBUG_DISPLAY_LIST
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index ccd3ba5..40a21e4 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -80,14 +80,6 @@
virtual void setViewport(int width, int height) = 0;
/**
- * Sets the position and size of the spot shadow casting light.
- *
- * @param lightCenter The light's Y position, relative to the render target's top left
- * @param lightRadius The light's radius
- */
- virtual void initializeLight(const Vector3& lightCenter, float lightRadius) = 0;
-
- /**
* Prepares the renderer to draw a frame. This method must be invoked
* at the beginning of each frame. When this method is invoked, the
* entire drawing surface is assumed to be redrawn.
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index bcfda99..e71439d 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -172,7 +172,8 @@
Vector2 centroid = poly[0];
if (area != 0) {
- centroid = Vector2(sumx / (3 * area), sumy / (3 * area));
+ centroid = (Vector2){static_cast<float>(sumx / (3 * area)),
+ static_cast<float>(sumy / (3 * area))};
} else {
ALOGW("Area is 0 while computing centroid!");
}
@@ -212,19 +213,19 @@
while (SkPath::kDone_Verb != (v = iter.next(pts))) {
switch (v) {
case SkPath::kMove_Verb:
- arrayForDirection.add(Vector2(pts[0].x(), pts[0].y()));
+ arrayForDirection.add((Vector2){pts[0].x(), pts[0].y()});
break;
case SkPath::kLine_Verb:
- arrayForDirection.add(Vector2(pts[1].x(), pts[1].y()));
+ arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
break;
case SkPath::kQuad_Verb:
- arrayForDirection.add(Vector2(pts[1].x(), pts[1].y()));
- arrayForDirection.add(Vector2(pts[2].x(), pts[2].y()));
+ arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
+ arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()});
break;
case SkPath::kCubic_Verb:
- arrayForDirection.add(Vector2(pts[1].x(), pts[1].y()));
- arrayForDirection.add(Vector2(pts[2].x(), pts[2].y()));
- arrayForDirection.add(Vector2(pts[3].x(), pts[3].y()));
+ arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
+ arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()});
+ arrayForDirection.add((Vector2){pts[3].x(), pts[3].y()});
break;
default:
break;
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index 06f6204..8c3077b 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -218,7 +218,7 @@
// Since neither polygon fully contain the other one, we need to add all the
// intersection points.
- Vector2 intersection;
+ Vector2 intersection = {0, 0};
for (int i = 0; i < poly2Length; i++) {
for (int j = 0; j < poly1Length; j++) {
int poly2LineStart = i;
@@ -250,7 +250,7 @@
}
// Sort the result polygon around the center.
- Vector2 center(0.0f, 0.0f);
+ Vector2 center = {0.0f, 0.0f};
for (int i = 0; i < count; i++) {
center += poly[i];
}
@@ -507,7 +507,6 @@
computeLightPolygon(lightVertexCount, lightCenter, lightSize, light);
computeSpotShadow(isCasterOpaque, light, lightVertexCount, lightCenter, poly,
polyLength, retStrips);
- retStrips.setMode(VertexBuffer::kTwoPolyRingShadow);
}
/**
@@ -558,7 +557,7 @@
float x = lightPoly[j].x - ratioZ * (lightPoly[j].x - poly[i].x);
float y = lightPoly[j].y - ratioZ * (lightPoly[j].y - poly[i].y);
- Vector2 newPoint = Vector2(x, y);
+ Vector2 newPoint = {x, y};
shadowRegion[k] = newPoint;
outline[m] = newPoint;
@@ -784,6 +783,9 @@
shadowVertices[2 * rays + rayIndex] = centroidXYA;
}
}
+
+ shadowTriangleStrip.setMode(VertexBuffer::kTwoPolyRingShadow);
+ shadowTriangleStrip.computeBounds<AlphaVertex>();
}
/**
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp
index 95c0ee5..140c6e8 100644
--- a/libs/hwui/StatefulBaseRenderer.cpp
+++ b/libs/hwui/StatefulBaseRenderer.cpp
@@ -23,10 +23,13 @@
namespace android {
namespace uirenderer {
-StatefulBaseRenderer::StatefulBaseRenderer() :
- mDirtyClip(false), mWidth(-1), mHeight(-1),
- mSaveCount(1), mFirstSnapshot(new Snapshot), mSnapshot(mFirstSnapshot),
- mLightCenter(FLT_MIN, FLT_MIN, FLT_MIN), mLightRadius(FLT_MIN) {
+StatefulBaseRenderer::StatefulBaseRenderer()
+ : mDirtyClip(false)
+ , mWidth(-1)
+ , mHeight(-1)
+ , mSaveCount(1)
+ , mFirstSnapshot(new Snapshot)
+ , mSnapshot(mFirstSnapshot) {
}
void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop,
@@ -45,11 +48,6 @@
onViewportInitialized();
}
-void StatefulBaseRenderer::initializeLight(const Vector3& lightCenter, float lightRadius) {
- mLightCenter = lightCenter;
- mLightRadius = lightRadius;
-}
-
///////////////////////////////////////////////////////////////////////////////
// Save (layer)
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
index e8e024f..25cc832 100644
--- a/libs/hwui/StatefulBaseRenderer.h
+++ b/libs/hwui/StatefulBaseRenderer.h
@@ -52,7 +52,6 @@
* the render target.
*/
virtual void setViewport(int width, int height);
- virtual void initializeLight(const Vector3& lightCenter, float lightRadius);
void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom);
// getters
@@ -161,10 +160,6 @@
// Current state
// TODO: should become private, once hooks needed by OpenGLRenderer are added
sp<Snapshot> mSnapshot;
-
- Vector3 mLightCenter;
- float mLightRadius;
-
}; // class StatefulBaseRenderer
}; // namespace uirenderer
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 08b54ff..c5fc21f 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -125,7 +125,7 @@
}
};
-struct TessellationCache::Buffer {
+class TessellationCache::Buffer {
public:
Buffer(const sp<Task<VertexBuffer*> >& task)
: mTask(task)
@@ -236,7 +236,7 @@
float maxZ = -FLT_MAX;
for (int i = 0; i < casterVertexCount; i++) {
const Vertex& point2d = casterVertices2d[i];
- casterPolygon[i] = Vector3(point2d.x, point2d.y, 0);
+ casterPolygon[i] = (Vector3){point2d.x, point2d.y, 0};
mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
minZ = fmin(minZ, casterPolygon[i].z);
maxZ = fmax(maxZ, casterPolygon[i].z);
@@ -246,7 +246,7 @@
Vector2 centroid = ShadowTessellator::centroid2d(
reinterpret_cast<const Vector2*>(casterVertices2d.array()),
casterVertexCount);
- Vector3 centroid3d(centroid.x, centroid.y, 0);
+ Vector3 centroid3d = {centroid.x, centroid.y, 0};
mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
// if the caster intersects the z=0 plane, lift it in Z so it doesn't
@@ -273,8 +273,6 @@
isCasterOpaque, casterPolygon, casterVertexCount,
*drawTransform, lightCenter, lightRadius, casterBounds, *localClip,
spotBuffer);
-
- // TODO: set ambientBuffer & spotBuffer's bounds for correct layer damage
}
class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t*> {
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index c61cb61..2a9f01c 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -24,18 +24,11 @@
// Classes
///////////////////////////////////////////////////////////////////////////////
+// MUST BE A POD - this means no ctor or dtor!
struct Vector2 {
float x;
float y;
- Vector2() :
- x(0.0f), y(0.0f) {
- }
-
- Vector2(float px, float py) :
- x(px), y(py) {
- }
-
float lengthSquared() const {
return x * x + y * y;
}
@@ -75,19 +68,19 @@
}
Vector2 operator+(const Vector2& v) const {
- return Vector2(x + v.x, y + v.y);
+ return (Vector2){x + v.x, y + v.y};
}
Vector2 operator-(const Vector2& v) const {
- return Vector2(x - v.x, y - v.y);
+ return (Vector2){x - v.x, y - v.y};
}
Vector2 operator/(float s) const {
- return Vector2(x / s, y / s);
+ return (Vector2){x / s, y / s};
}
Vector2 operator*(float s) const {
- return Vector2(x * s, y * s);
+ return (Vector2){x * s, y * s};
}
void normalize() {
@@ -97,7 +90,7 @@
}
Vector2 copyNormalized() const {
- Vector2 v(x, y);
+ Vector2 v = {x, y};
v.normalize();
return v;
}
@@ -111,31 +104,18 @@
}
}; // class Vector2
+// MUST BE A POD - this means no ctor or dtor!
class Vector3 {
public:
float x;
float y;
float z;
- Vector3() :
- x(0.0f), y(0.0f), z(0.0f) {
- }
-
- Vector3(float px, float py, float pz) :
- x(px), y(py), z(pz) {
- }
-
void dump() {
ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z);
}
};
-///////////////////////////////////////////////////////////////////////////////
-// Types
-///////////////////////////////////////////////////////////////////////////////
-
-typedef Vector2 vec2;
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index 5d7a199..4ff0b18 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -43,7 +43,7 @@
vertex[0].y = y;
}
- static inline void set(Vertex* vertex, vec2 val) {
+ static inline void set(Vertex* vertex, Vector2 val) {
set(vertex, val.x, val.y);
}
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
index 55d566b..3837f88 100644
--- a/libs/hwui/VertexBuffer.h
+++ b/libs/hwui/VertexBuffer.h
@@ -62,6 +62,7 @@
mVertexCount = vertexCount;
mByteCount = mVertexCount * sizeof(TYPE);
mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount];
+
mCleanupMethod = &(cleanup<TYPE>);
return (TYPE*)mBuffer;
@@ -79,6 +80,28 @@
}
}
+ /**
+ * Brute force bounds computation, used only if the producer of this
+ * vertex buffer can't determine bounds more simply/efficiently
+ */
+ template <class TYPE>
+ void computeBounds(int vertexCount = 0) {
+ if (!mVertexCount) {
+ mBounds.setEmpty();
+ return;
+ }
+
+ // default: compute over every vertex
+ if (vertexCount == 0) vertexCount = mVertexCount;
+
+ TYPE* current = (TYPE*)mBuffer;
+ TYPE* end = current + vertexCount;
+ mBounds.set(current->x, current->y, current->x, current->y);
+ for (; current < end; current++) {
+ mBounds.expandToCoverVertex(current->x, current->y);
+ }
+ }
+
const void* getBuffer() const { return mBuffer; }
const Rect& getBounds() const { return mBounds; }
unsigned int getVertexCount() const { return mVertexCount; }
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 2147810..756f660 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -110,10 +110,11 @@
// and such to prevent from trying to render into this surface
}
-void CanvasContext::setup(int width, int height, const Vector3& lightCenter, float lightRadius) {
+void CanvasContext::setup(int width, int height, const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
if (!mCanvas) return;
mCanvas->setViewport(width, height);
- mCanvas->initializeLight(lightCenter, lightRadius);
+ mCanvas->initLight(lightCenter, lightRadius, ambientShadowAlpha, spotShadowAlpha);
}
void CanvasContext::setOpaque(bool opaque) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 1bab1b1..2a01027 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -53,7 +53,8 @@
bool initialize(ANativeWindow* window);
void updateSurface(ANativeWindow* window);
void pauseSurface(ANativeWindow* window);
- void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
+ void setup(int width, int height, const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
void setOpaque(bool opaque);
void makeCurrent();
void processLayerUpdate(DeferredLayerUpdater* layerUpdater);
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index dd34e09..763e727 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -85,8 +85,7 @@
void DrawFrameTask::postAndWait() {
AutoMutex _lock(mLock);
- mRenderThread->queue(this);
- mSignal.wait(mLock);
+ mRenderThread->queueAndWait(this, mSignal, mLock);
}
void DrawFrameTask::run() {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 6cd3d0b..1e91eb5 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -39,6 +39,8 @@
#define CREATE_BRIDGE3(name, a1, a2, a3) CREATE_BRIDGE(name, a1,a2,a3,,,,,)
#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1,a2,a3,a4,,,,)
#define CREATE_BRIDGE5(name, a1, a2, a3, a4, a5) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,,,)
+#define CREATE_BRIDGE6(name, a1, a2, a3, a4, a5, a6) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,a6,,)
+#define CREATE_BRIDGE7(name, a1, a2, a3, a4, a5, a6, a7) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,a6,a7,)
#define CREATE_BRIDGE(name, a1, a2, a3, a4, a5, a6, a7, a8) \
typedef struct { \
a1; a2; a3; a4; a5; a6; a7; a8; \
@@ -152,19 +154,24 @@
postAndWait(task);
}
-CREATE_BRIDGE5(setup, CanvasContext* context, int width, int height,
- Vector3 lightCenter, int lightRadius) {
- args->context->setup(args->width, args->height, args->lightCenter, args->lightRadius);
+CREATE_BRIDGE7(setup, CanvasContext* context, int width, int height,
+ Vector3 lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
+ args->context->setup(args->width, args->height, args->lightCenter, args->lightRadius,
+ args->ambientShadowAlpha, args->spotShadowAlpha);
return NULL;
}
-void RenderProxy::setup(int width, int height, const Vector3& lightCenter, float lightRadius) {
+void RenderProxy::setup(int width, int height, const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
SETUP_TASK(setup);
args->context = mContext;
args->width = width;
args->height = height;
args->lightCenter = lightCenter;
args->lightRadius = lightRadius;
+ args->ambientShadowAlpha = ambientShadowAlpha;
+ args->spotShadowAlpha = spotShadowAlpha;
post(task);
}
@@ -405,8 +412,7 @@
task->setReturnPtr(&retval);
SignalingRenderTask syncTask(task, &mSyncMutex, &mSyncCondition);
AutoMutex _lock(mSyncMutex);
- mRenderThread.queue(&syncTask);
- mSyncCondition.wait(mSyncMutex);
+ mRenderThread.queueAndWait(&syncTask, mSyncCondition, mSyncMutex);
return retval;
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 0027403..28d0173 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -67,7 +67,8 @@
ANDROID_API bool initialize(const sp<ANativeWindow>& window);
ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
- ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
+ ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
ANDROID_API void setOpaque(bool opaque);
ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
float density);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 03e98d5..32dc46e 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -20,6 +20,7 @@
#include <gui/DisplayEventReceiver.h>
#include <utils/Log.h>
+#include <pthread.h>
#include "../RenderState.h"
#include "CanvasContext.h"
@@ -136,6 +137,7 @@
};
RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
+ , mThreadId(0)
, mNextWakeup(LLONG_MAX)
, mDisplayEventReceiver(0)
, mVsyncRequested(false)
@@ -244,6 +246,7 @@
}
bool RenderThread::threadLoop() {
+ mThreadId = pthread_self();
initThreadLocals();
int timeoutMillis = -1;
@@ -289,6 +292,16 @@
}
}
+void RenderThread::queueAndWait(RenderTask* task, Condition& signal, Mutex& lock) {
+ static nsecs_t sTimeout = milliseconds(500);
+ queue(task);
+ status_t err = signal.waitRelative(lock, sTimeout);
+ if (CC_UNLIKELY(err != NO_ERROR)) {
+ ALOGE("Timeout waiting for RenderTherad! err=%d", err);
+ nukeFromOrbit();
+ }
+}
+
void RenderThread::queueAtFront(RenderTask* task) {
AutoMutex _lock(mLock);
mQueue.queueAtFront(task);
@@ -341,6 +354,10 @@
return next;
}
+void RenderThread::nukeFromOrbit() {
+ pthread_kill(mThreadId, SIGABRT);
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 0b91e9d..5984373 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -23,6 +23,7 @@
#include <set>
#include <cutils/compiler.h>
+#include <utils/Condition.h>
#include <utils/Looper.h>
#include <utils/Mutex.h>
#include <utils/Singleton.h>
@@ -73,6 +74,7 @@
// RenderThread takes complete ownership of tasks that are queued
// and will delete them after they are run
ANDROID_API void queue(RenderTask* task);
+ void queueAndWait(RenderTask* task, Condition& signal, Mutex& lock);
ANDROID_API void queueAtFront(RenderTask* task);
void queueDelayed(RenderTask* task, int delayMs);
void remove(RenderTask* task);
@@ -106,11 +108,15 @@
void dispatchFrameCallbacks();
void requestVsync();
+ // VERY DANGEROUS HANDLE WITH EXTREME CARE
+ void nukeFromOrbit();
+
// Returns the next task to be run. If this returns NULL nextWakeup is set
// to the time to requery for the nextTask to run. mNextWakeup is also
// set to this time
RenderTask* nextTask(nsecs_t* nextWakeup);
+ pthread_t mThreadId;
sp<Looper> mLooper;
Mutex mLock;
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 65f1663..2dfe9c6 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -19,19 +19,19 @@
namespace android {
namespace uirenderer {
+#define NON_ZERO_EPSILON (0.001f)
+
class MathUtils {
-private:
- static const float gNonZeroEpsilon = 0.001f;
public:
/**
* Check for floats that are close enough to zero.
*/
inline static bool isZero(float value) {
- return (value >= -gNonZeroEpsilon) && (value <= gNonZeroEpsilon);
+ return (value >= -NON_ZERO_EPSILON) && (value <= NON_ZERO_EPSILON);
}
inline static bool isPositive(float value) {
- return value >= gNonZeroEpsilon;
+ return value >= NON_ZERO_EPSILON;
}
inline static bool areEqual(float valueA, float valueB) {
diff --git a/location/java/android/location/GpsClock.java b/location/java/android/location/GpsClock.java
index f327c4e..963b604 100644
--- a/location/java/android/location/GpsClock.java
+++ b/location/java/android/location/GpsClock.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
/**
* A class containing a GPS clock timestamp.
@@ -26,44 +27,65 @@
* @hide
*/
public class GpsClock implements Parcelable {
- // mandatory parameters
- private long mTimeInNs;
+ private static final String TAG = "GpsClock";
- // optional parameters
- private boolean mHasLeapSecond;
+ // The following enumerations must be in sync with the values declared in gps.h
+
+ /**
+ * The type of the time stored is not available or it is unknown.
+ */
+ public static final byte TYPE_UNKNOWN = 0;
+
+ /**
+ * The source of the time value reported by this class is the 'Local Hardware Clock'.
+ */
+ public static final byte TYPE_LOCAL_HW_TIME = 1;
+
+ /**
+ * The source of the time value reported by this class is the 'GPS time' derived from
+ * satellites (epoch = Jan 6, 1980).
+ */
+ public static final byte TYPE_GPS_TIME = 2;
+
+ private static final short HAS_NO_FLAGS = 0;
+ private static final short HAS_LEAP_SECOND = (1<<0);
+ private static final short HAS_TIME_UNCERTAINTY = (1<<1);
+ private static final short HAS_FULL_BIAS = (1<<2);
+ private static final short HAS_BIAS = (1<<3);
+ private static final short HAS_BIAS_UNCERTAINTY = (1<<4);
+ private static final short HAS_DRIFT = (1<<5);
+ private static final short HAS_DRIFT_UNCERTAINTY = (1<<6);
+
+ // End enumerations in sync with gps.h
+
+ private short mFlags;
private short mLeapSecond;
- private boolean mHasTimeUncertaintyInNs;
+ private byte mType;
+ private long mTimeInNs;
private double mTimeUncertaintyInNs;
- private boolean mHasBiasInNs;
+ private long mFullBiasInNs;
private double mBiasInNs;
- private boolean mHasBiasUncertaintyInNs;
private double mBiasUncertaintyInNs;
- private boolean mHasDriftInNsPerSec;
private double mDriftInNsPerSec;
- private boolean mHasDriftUncertaintyInNsPerSec;
private double mDriftUncertaintyInNsPerSec;
GpsClock() {
- reset();
+ initialize();
}
/**
* Sets all contents to the values stored in the provided object.
*/
public void set(GpsClock clock) {
- mTimeInNs = clock.mTimeInNs;
-
- mHasLeapSecond = clock.mHasLeapSecond;
+ mFlags = clock.mFlags;
mLeapSecond = clock.mLeapSecond;
- mHasTimeUncertaintyInNs = clock.mHasTimeUncertaintyInNs;
+ mType = clock.mType;
+ mTimeInNs = clock.mTimeInNs;
mTimeUncertaintyInNs = clock.mTimeUncertaintyInNs;
- mHasBiasInNs = clock.mHasBiasInNs;
+ mFullBiasInNs = clock.mFullBiasInNs;
mBiasInNs = clock.mBiasInNs;
- mHasBiasUncertaintyInNs = clock.mHasBiasUncertaintyInNs;
mBiasUncertaintyInNs = clock.mBiasUncertaintyInNs;
- mHasDriftInNsPerSec = clock.mHasDriftInNsPerSec;
mDriftInNsPerSec = clock.mDriftInNsPerSec;
- mHasDriftUncertaintyInNsPerSec = clock.mHasDriftUncertaintyInNsPerSec;
mDriftUncertaintyInNsPerSec = clock.mDriftUncertaintyInNsPerSec;
}
@@ -71,25 +93,61 @@
* Resets all the contents to its original state.
*/
public void reset() {
- mTimeInNs = Long.MIN_VALUE;
+ initialize();
+ }
- resetLeapSecond();
- resetTimeUncertaintyInNs();
- resetBiasInNs();
- resetBiasUncertaintyInNs();
- resetDriftInNsPerSec();
- resetDriftUncertaintyInNsPerSec();
+ /**
+ * Gets the type of time reported by {@link #getTimeInNs()}.
+ */
+ public byte getType() {
+ return mType;
+ }
+
+ /**
+ * Sets the type of time reported.
+ */
+ public void setType(byte value) {
+ switch (value) {
+ case TYPE_UNKNOWN:
+ case TYPE_GPS_TIME:
+ case TYPE_LOCAL_HW_TIME:
+ mType = value;
+ break;
+ default:
+ Log.d(TAG, "Sanitizing invalid 'type': " + value);
+ mType = TYPE_UNKNOWN;
+ break;
+ }
+ }
+
+ /**
+ * Gets a string representation of the 'type'.
+ * For internal and logging use only.
+ */
+ private String getTypeString() {
+ switch (mType) {
+ case TYPE_UNKNOWN:
+ return "Unknown";
+ case TYPE_GPS_TIME:
+ return "GpsTime";
+ case TYPE_LOCAL_HW_TIME:
+ return "LocalHwClock";
+ default:
+ return "<Invalid>";
+ }
}
/**
* Returns true if {@link #getLeapSecond()} is available, false otherwise.
*/
public boolean hasLeapSecond() {
- return mHasLeapSecond;
+ return isFlagSet(HAS_LEAP_SECOND);
}
/**
* Gets the leap second associated with the clock's time.
+ * The sign of the value is defined by the following equation:
+ * utc_time_ns = time_ns + (full_bias_ns + bias_ns) - leap_second * 1,000,000,000
*
* The value is only available if {@link #hasLeapSecond()} is true.
*/
@@ -101,7 +159,7 @@
* Sets the leap second associated with the clock's time.
*/
public void setLeapSecond(short leapSecond) {
- mHasLeapSecond = true;
+ setFlag(HAS_LEAP_SECOND);
mLeapSecond = leapSecond;
}
@@ -109,13 +167,25 @@
* Resets the leap second associated with the clock's time.
*/
public void resetLeapSecond() {
- mHasLeapSecond = false;
+ resetFlag(HAS_LEAP_SECOND);
mLeapSecond = Short.MIN_VALUE;
}
/**
- * Gets the GPS clock Time in nanoseconds; it represents the uncorrected receiver's GPS time
- * since 0000Z, January 6, 1980; this is, including {@link #getBiasInNs()}.
+ * Gets the GPS receiver internal clock value in nanoseconds.
+ * This can be either the 'local hardware clock' value ({@link #TYPE_LOCAL_HW_TIME}), or the
+ * current GPS time derived inside GPS receiver ({@link #TYPE_GPS_TIME}).
+ * {@link #getType()} defines the time reported.
+ *
+ * For 'local hardware clock' this value is expected to be monotonically increasing during the
+ * reporting session. The real GPS time can be derived by compensating
+ * {@link #getFullBiasInNs()} (when it is available) from this value.
+ *
+ * For 'GPS time' this value is expected to be the best estimation of current GPS time that GPS
+ * receiver can achieve. {@link #getTimeUncertaintyInNs()} should be available when GPS time is
+ * specified.
+ *
+ * Sub-nanosecond accuracy can be provided by means of {@link #getBiasInNs()}.
* The reported time includes {@link #getTimeUncertaintyInNs()}.
*/
public long getTimeInNs() {
@@ -123,7 +193,7 @@
}
/**
- * Sets the GPS clock Time in nanoseconds.
+ * Sets the GPS receiver internal clock in nanoseconds.
*/
public void setTimeInNs(long timeInNs) {
mTimeInNs = timeInNs;
@@ -133,11 +203,12 @@
* Returns true if {@link #getTimeUncertaintyInNs()} is available, false otherwise.
*/
public boolean hasTimeUncertaintyInNs() {
- return mHasTimeUncertaintyInNs;
+ return isFlagSet(HAS_TIME_UNCERTAINTY);
}
/**
* Gets the clock's time Uncertainty (1-Sigma) in nanoseconds.
+ * The uncertainty is represented as an absolute (single sided) value.
*
* The value is only available if {@link #hasTimeUncertaintyInNs()} is true.
*/
@@ -149,7 +220,7 @@
* Sets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
*/
public void setTimeUncertaintyInNs(double timeUncertaintyInNs) {
- mHasTimeUncertaintyInNs = true;
+ setFlag(HAS_TIME_UNCERTAINTY);
mTimeUncertaintyInNs = timeUncertaintyInNs;
}
@@ -157,34 +228,73 @@
* Resets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
*/
public void resetTimeUncertaintyInNs() {
- mHasTimeUncertaintyInNs = false;
+ resetFlag(HAS_TIME_UNCERTAINTY);
mTimeUncertaintyInNs = Double.NaN;
}
/**
+ * Returns true if {@link @getFullBiasInNs()} is available, false otherwise.
+ */
+ public boolean hasFullBiasInNs() {
+ return isFlagSet(HAS_FULL_BIAS);
+ }
+
+ /**
+ * Gets the difference between hardware clock ({@link #getTimeInNs()}) inside GPS receiver and
+ * the true GPS time since 0000Z, January 6, 1980, in nanoseconds.
+ *
+ * This value is available if {@link #TYPE_LOCAL_HW_TIME} is set, and GPS receiver has solved
+ * the clock for GPS time.
+ * {@link #getBiasUncertaintyInNs()} should be used for quality check.
+ *
+ * The sign of the value is defined by the following equation:
+ * true time (GPS time) = time_ns + (full_bias_ns + bias_ns)
+ *
+ * The reported full bias includes {@link #getBiasUncertaintyInNs()}.
+ * The value is onl available if {@link #hasFullBiasInNs()} is true.
+ */
+ public long getFullBiasInNs() {
+ return mFullBiasInNs;
+ }
+
+ /**
+ * Sets the full bias in nanoseconds.
+ */
+ public void setFullBiasInNs(long value) {
+ setFlag(HAS_FULL_BIAS);
+ mFullBiasInNs = value;
+ }
+
+ /**
+ * Resets the full bias in nanoseconds.
+ */
+ public void resetFullBiasInNs() {
+ resetFlag(HAS_FULL_BIAS);
+ mFullBiasInNs = Long.MIN_VALUE;
+ }
+
+ /**
* Returns true if {@link #getBiasInNs()} is available, false otherwise.
*/
public boolean hasBiasInNs() {
- return mHasBiasInNs;
+ return isFlagSet(HAS_BIAS);
}
/**
- * Gets the clock's Bias in nanoseconds.
- * The sign of the value (if available), is defined by the following equation:
- * true time = time - bias.
+ * Gets the clock's sub-nanosecond bias.
* The reported bias includes {@link #getBiasUncertaintyInNs()}.
*
* The value is only available if {@link #hasBiasInNs()} is true.
*/
- public Double getBiasInNs() {
+ public double getBiasInNs() {
return mBiasInNs;
}
/**
- * Sets the clock's Bias in nanoseconds.
+ * Sets the sub-nanosecond bias.
*/
public void setBiasInNs(double biasInNs) {
- mHasBiasInNs = true;
+ setFlag(HAS_BIAS);
mBiasInNs = biasInNs;
}
@@ -192,7 +302,7 @@
* Resets the clock's Bias in nanoseconds.
*/
public void resetBiasInNs() {
- mHasBiasInNs = false;
+ resetFlag(HAS_BIAS);
mBiasInNs = Double.NaN;
}
@@ -200,7 +310,7 @@
* Returns true if {@link #getBiasUncertaintyInNs()} is available, false otherwise.
*/
public boolean hasBiasUncertaintyInNs() {
- return mHasBiasUncertaintyInNs;
+ return isFlagSet(HAS_BIAS_UNCERTAINTY);
}
/**
@@ -216,7 +326,7 @@
* Sets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
*/
public void setBiasUncertaintyInNs(double biasUncertaintyInNs) {
- mHasBiasUncertaintyInNs = true;
+ setFlag(HAS_BIAS_UNCERTAINTY);
mBiasUncertaintyInNs = biasUncertaintyInNs;
}
@@ -224,7 +334,7 @@
* Resets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
*/
public void resetBiasUncertaintyInNs() {
- mHasBiasUncertaintyInNs = false;
+ resetFlag(HAS_BIAS_UNCERTAINTY);
mBiasUncertaintyInNs = Double.NaN;
}
@@ -232,7 +342,7 @@
* Returns true if {@link #getDriftInNsPerSec()} is available, false otherwise.
*/
public boolean hasDriftInNsPerSec() {
- return mHasDriftInNsPerSec;
+ return isFlagSet(HAS_DRIFT);
}
/**
@@ -250,7 +360,7 @@
* Sets the clock's Drift in nanoseconds per second.
*/
public void setDriftInNsPerSec(double driftInNsPerSec) {
- mHasDriftInNsPerSec = true;
+ setFlag(HAS_DRIFT);
mDriftInNsPerSec = driftInNsPerSec;
}
@@ -258,7 +368,7 @@
* Resets the clock's Drift in nanoseconds per second.
*/
public void resetDriftInNsPerSec() {
- mHasDriftInNsPerSec = false;
+ resetFlag(HAS_DRIFT);
mDriftInNsPerSec = Double.NaN;
}
@@ -266,7 +376,7 @@
* Returns true if {@link #getDriftUncertaintyInNsPerSec()} is available, false otherwise.
*/
public boolean hasDriftUncertaintyInNsPerSec() {
- return mHasDriftUncertaintyInNsPerSec;
+ return isFlagSet(HAS_DRIFT_UNCERTAINTY);
}
/**
@@ -282,7 +392,7 @@
* Sets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
*/
public void setDriftUncertaintyInNsPerSec(double driftUncertaintyInNsPerSec) {
- mHasDriftUncertaintyInNsPerSec = true;
+ setFlag(HAS_DRIFT_UNCERTAINTY);
mDriftUncertaintyInNsPerSec = driftUncertaintyInNsPerSec;
}
@@ -290,7 +400,7 @@
* Resets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
*/
public void resetDriftUncertaintyInNsPerSec() {
- mHasDriftUncertaintyInNsPerSec = false;
+ resetFlag(HAS_DRIFT_UNCERTAINTY);
mDriftUncertaintyInNsPerSec = Double.NaN;
}
@@ -298,19 +408,16 @@
@Override
public GpsClock createFromParcel(Parcel parcel) {
GpsClock gpsClock = new GpsClock();
- gpsClock.mTimeInNs = parcel.readLong();
- gpsClock.mHasLeapSecond = parcel.readInt() != 0;
+ gpsClock.mFlags = (short) parcel.readInt();
gpsClock.mLeapSecond = (short) parcel.readInt();
- gpsClock.mHasTimeUncertaintyInNs = parcel.readInt() != 0;
+ gpsClock.mType = parcel.readByte();
+ gpsClock.mTimeInNs = parcel.readLong();
gpsClock.mTimeUncertaintyInNs = parcel.readDouble();
- gpsClock.mHasBiasInNs = parcel.readInt() != 0;
+ gpsClock.mFullBiasInNs = parcel.readLong();
gpsClock.mBiasInNs = parcel.readDouble();
- gpsClock.mHasBiasUncertaintyInNs = parcel.readInt() != 0;
gpsClock.mBiasUncertaintyInNs = parcel.readDouble();
- gpsClock.mHasDriftInNsPerSec = parcel.readInt() != 0;
gpsClock.mDriftInNsPerSec = parcel.readDouble();
- gpsClock.mHasDriftUncertaintyInNsPerSec = parcel.readInt() != 0;
gpsClock.mDriftUncertaintyInNsPerSec = parcel.readDouble();
return gpsClock;
@@ -323,19 +430,15 @@
};
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeLong(mTimeInNs);
-
- parcel.writeInt(mHasLeapSecond ? 1 : 0);
+ parcel.writeInt(mFlags);
parcel.writeInt(mLeapSecond);
- parcel.writeInt(mHasTimeUncertaintyInNs ? 1 : 0);
+ parcel.writeByte(mType);
+ parcel.writeLong(mTimeInNs);
parcel.writeDouble(mTimeUncertaintyInNs);
- parcel.writeInt(mHasBiasInNs ? 1 : 0);
+ parcel.writeLong(mFullBiasInNs);
parcel.writeDouble(mBiasInNs);
- parcel.writeInt(mHasBiasUncertaintyInNs ? 1 : 0);
parcel.writeDouble(mBiasUncertaintyInNs);
- parcel.writeInt(mHasDriftInNsPerSec ? 1 : 0);
parcel.writeDouble(mDriftInNsPerSec);
- parcel.writeInt(mHasDriftUncertaintyInNsPerSec ? 1 : 0);
parcel.writeDouble(mDriftUncertaintyInNsPerSec);
}
@@ -346,37 +449,64 @@
@Override
public String toString() {
- final String format = " %-15s = %-25s %-26s = %s\n";
+ final String format = " %-15s = %s\n";
+ final String formatWithUncertainty = " %-15s = %-25s %-26s = %s\n";
StringBuilder builder = new StringBuilder("GpsClock:\n");
- builder.append(String.format(
- format,
- "LeapSecond",
- mHasLeapSecond ? mLeapSecond : null,
- "",
- ""));
+ builder.append(String.format(format, "Type", getTypeString()));
+
+ builder.append(String.format(format, "LeapSecond", hasLeapSecond() ? mLeapSecond : null));
builder.append(String.format(
- format,
+ formatWithUncertainty,
"TimeInNs",
mTimeInNs,
"TimeUncertaintyInNs",
- mHasTimeUncertaintyInNs ? mTimeUncertaintyInNs : null));
+ hasTimeUncertaintyInNs() ? mTimeUncertaintyInNs : null));
builder.append(String.format(
format,
+ "FullBiasInNs",
+ hasFullBiasInNs() ? mFullBiasInNs : null));
+
+ builder.append(String.format(
+ formatWithUncertainty,
"BiasInNs",
- mHasBiasInNs ? mBiasInNs : null,
+ hasBiasInNs() ? mBiasInNs : null,
"BiasUncertaintyInNs",
- mHasBiasUncertaintyInNs ? mBiasUncertaintyInNs : null));
+ hasBiasUncertaintyInNs() ? mBiasUncertaintyInNs : null));
builder.append(String.format(
- format,
+ formatWithUncertainty,
"DriftInNsPerSec",
- mHasDriftInNsPerSec ? mDriftInNsPerSec : null,
+ hasDriftInNsPerSec() ? mDriftInNsPerSec : null,
"DriftUncertaintyInNsPerSec",
- mHasDriftUncertaintyInNsPerSec ? mDriftUncertaintyInNsPerSec : null));
+ hasDriftUncertaintyInNsPerSec() ? mDriftUncertaintyInNsPerSec : null));
return builder.toString();
}
+
+ private void initialize() {
+ mFlags = HAS_NO_FLAGS;
+ resetLeapSecond();
+ setType(TYPE_UNKNOWN);
+ setTimeInNs(Long.MIN_VALUE);
+ resetTimeUncertaintyInNs();
+ resetBiasInNs();
+ resetBiasUncertaintyInNs();
+ resetDriftInNsPerSec();
+ resetDriftUncertaintyInNsPerSec();
+ }
+
+ private void setFlag(short flag) {
+ mFlags |= flag;
+ }
+
+ private void resetFlag(short flag) {
+ mFlags &= ~flag;
+ }
+
+ private boolean isFlagSet(short flag) {
+ return (mFlags & flag) == flag;
+ }
}
diff --git a/location/java/android/location/GpsMeasurement.java b/location/java/android/location/GpsMeasurement.java
index e5a8c8c..591a6ca 100644
--- a/location/java/android/location/GpsMeasurement.java
+++ b/location/java/android/location/GpsMeasurement.java
@@ -28,143 +28,177 @@
public class GpsMeasurement implements Parcelable {
private static final String TAG = "GpsMeasurement";
- // mandatory parameters
+ private int mFlags;
private byte mPrn;
- private long mLocalTimeInNs;
+ private double mTimeOffsetInNs;
+ private short mState;
private long mReceivedGpsTowInNs;
+ private long mReceivedGpsTowUncertaintyInNs;
private double mCn0InDbHz;
private double mPseudorangeRateInMetersPerSec;
private double mPseudorangeRateUncertaintyInMetersPerSec;
+ private short mAccumulatedDeltaRangeState;
private double mAccumulatedDeltaRangeInMeters;
private double mAccumulatedDeltaRangeUncertaintyInMeters;
-
- // optional parameters
- private boolean mHasPseudorangeInMeters;
private double mPseudorangeInMeters;
- private boolean mHasPseudorangeUncertaintyInMeters;
private double mPseudorangeUncertaintyInMeters;
- private boolean mHasCodePhaseInChips;
private double mCodePhaseInChips;
- private boolean mHasCodePhaseUncertaintyInChips;
private double mCodePhaseUncertaintyInChips;
- private boolean mHasCarrierFrequencyInHz;
private float mCarrierFrequencyInHz;
- private boolean mHasCarrierCycles;
private long mCarrierCycles;
- private boolean mHasCarrierPhase;
private double mCarrierPhase;
- private boolean mHasCarrierPhaseUncertainty;
private double mCarrierPhaseUncertainty;
- private short mLossOfLock;
- private boolean mHasBitNumber;
+ private byte mLossOfLock;
private short mBitNumber;
- private boolean mHasTimeFromLastBitInNs;
- private long mTimeFromLastBitInNs;
- private boolean mHasDopplerShiftInHz;
+ private short mTimeFromLastBitInMs;
private double mDopplerShiftInHz;
- private boolean mHasDopplerShiftUncertaintyInHz;
private double mDopplerShiftUncertaintyInHz;
- private short mMultipathIndicator;
- private boolean mHasSnrInDb;
+ private byte mMultipathIndicator;
private double mSnrInDb;
- private boolean mHasElevationInDeg;
private double mElevationInDeg;
- private boolean mHasElevationUncertaintyInDeg;
private double mElevationUncertaintyInDeg;
- private boolean mHasAzimuthInDeg;
private double mAzimuthInDeg;
- private boolean mHasAzimuthUncertaintyInDeg;
private double mAzimuthUncertaintyInDeg;
private boolean mUsedInFix;
// The following enumerations must be in sync with the values declared in gps.h
+ private static final int HAS_NO_FLAGS = 0;
+ private static final int HAS_SNR = (1<<0);
+ private static final int HAS_ELEVATION = (1<<1);
+ private static final int HAS_ELEVATION_UNCERTAINTY = (1<<2);
+ private static final int HAS_AZIMUTH = (1<<3);
+ private static final int HAS_AZIMUTH_UNCERTAINTY = (1<<4);
+ private static final int HAS_PSEUDORANGE = (1<<5);
+ private static final int HAS_PSEUDORANGE_UNCERTAINTY = (1<<6);
+ private static final int HAS_CODE_PHASE = (1<<7);
+ private static final int HAS_CODE_PHASE_UNCERTAINTY = (1<<8);
+ private static final int HAS_CARRIER_FREQUENCY = (1<<9);
+ private static final int HAS_CARRIER_CYCLES = (1<<10);
+ private static final int HAS_CARRIER_PHASE = (1<<11);
+ private static final int HAS_CARRIER_PHASE_UNCERTAINTY = (1<<12);
+ private static final int HAS_BIT_NUMBER = (1<<13);
+ private static final int HAS_TIME_FROM_LAST_BIT = (1<<14);
+ private static final int HAS_DOPPLER_SHIFT = (1<<15);
+ private static final int HAS_DOPPLER_SHIFT_UNCERTAINTY = (1<<16);
+
/**
* The indicator is not available or it is unknown.
*/
- public static final short LOSS_OF_LOCK_UNKNOWN = 0;
+ public static final byte LOSS_OF_LOCK_UNKNOWN = 0;
/**
* The measurement does not present any indication of 'loss of lock'.
*/
- public static final short LOSS_OF_LOCK_OK = 1;
+ public static final byte LOSS_OF_LOCK_OK = 1;
/**
* 'Loss of lock' detected between the previous and current observation: cycle slip possible.
*/
- public static final short LOSS_OF_LOCK_CYCLE_SLIP = 2;
+ public static final byte LOSS_OF_LOCK_CYCLE_SLIP = 2;
/**
* The indicator is not available or it is unknown.
*/
- public static final short MULTIPATH_INDICATOR_UNKNOWN = 0;
+ public static final byte MULTIPATH_INDICATOR_UNKNOWN = 0;
/**
* The measurement has been indicated to use multi-path.
*/
- public static final short MULTIPATH_INDICATOR_DETECTED = 1;
+ public static final byte MULTIPATH_INDICATOR_DETECTED = 1;
/**
* The measurement has been indicated not tu use multi-path.
*/
- public static final short MULTIPATH_INDICATOR_NOT_USED = 2;
+ public static final byte MULTIPATH_INDICATOR_NOT_USED = 2;
+
+ /**
+ * The state of GPS receiver the measurement is invalid or unknown.
+ */
+ public static final short STATE_UNKNOWN = 0;
+
+ /**
+ * The state of the GPS receiver is ranging code lock.
+ */
+ public static final short STATE_CODE_LOCK = (1<<0);
+
+ /**
+ * The state of the GPS receiver is in bit sync.
+ */
+ public static final short STATE_BIT_SYNC = (1<<1);
+
+ /**
+ *The state of the GPS receiver is in sub-frame sync.
+ */
+ public static final short STATE_SUBFRAME_SYNC = (1<<2);
+
+ /**
+ * The state of the GPS receiver has TOW decoded.
+ */
+ public static final short STATE_TOW_DECODED = (1<<3);
+
+ /**
+ * The state of the 'Accumulated Delta Range' is invalid or unknown.
+ */
+ public static final short ADR_STATE_UNKNOWN = 0;
+
+ /**
+ * The state of the 'Accumulated Delta Range' is valid.
+ */
+ public static final short ADR_STATE_VALID = (1<<0);
+
+ /**
+ * The state of the 'Accumulated Delta Range' has detected a reset.
+ */
+ public static final short ADR_STATE_RESET = (1<<1);
+
+ /**
+ * The state of the 'Accumulated Delta Range' has a cycle slip detected.
+ */
+ public static final short ADR_STATE_CYCLE_SLIP = (1<<2);
// End enumerations in sync with gps.h
GpsMeasurement() {
- reset();
+ initialize();
}
/**
* Sets all contents to the values stored in the provided object.
*/
public void set(GpsMeasurement measurement) {
+ mFlags = measurement.mFlags;
mPrn = measurement.mPrn;
- mLocalTimeInNs = measurement.mLocalTimeInNs;
+ mTimeOffsetInNs = measurement.mTimeOffsetInNs;
+ mState = measurement.mState;
mReceivedGpsTowInNs = measurement.mReceivedGpsTowInNs;
+ mReceivedGpsTowUncertaintyInNs = measurement.mReceivedGpsTowUncertaintyInNs;
mCn0InDbHz = measurement.mCn0InDbHz;
mPseudorangeRateInMetersPerSec = measurement.mPseudorangeRateInMetersPerSec;
mPseudorangeRateUncertaintyInMetersPerSec =
measurement.mPseudorangeRateUncertaintyInMetersPerSec;
+ mAccumulatedDeltaRangeState = measurement.mAccumulatedDeltaRangeState;
mAccumulatedDeltaRangeInMeters = measurement.mAccumulatedDeltaRangeInMeters;
mAccumulatedDeltaRangeUncertaintyInMeters =
measurement.mAccumulatedDeltaRangeUncertaintyInMeters;
-
- mHasPseudorangeInMeters = measurement.mHasPseudorangeInMeters;
mPseudorangeInMeters = measurement.mPseudorangeInMeters;
- mHasPseudorangeUncertaintyInMeters = measurement.mHasPseudorangeUncertaintyInMeters;
mPseudorangeUncertaintyInMeters = measurement.mPseudorangeUncertaintyInMeters;
- mHasCodePhaseInChips = measurement.mHasCodePhaseInChips;
mCodePhaseInChips = measurement.mCodePhaseInChips;
- mHasCodePhaseUncertaintyInChips = measurement.mHasCodePhaseUncertaintyInChips;
mCodePhaseUncertaintyInChips = measurement.mCodePhaseUncertaintyInChips;
- mHasCarrierFrequencyInHz = measurement.mHasCarrierFrequencyInHz;
mCarrierFrequencyInHz = measurement.mCarrierFrequencyInHz;
- mHasCarrierCycles = measurement.mHasCarrierCycles;
mCarrierCycles = measurement.mCarrierCycles;
- mHasCarrierPhase = measurement.mHasCarrierPhase;
mCarrierPhase = measurement.mCarrierPhase;
- mHasCarrierPhaseUncertainty = measurement.mHasCarrierPhaseUncertainty;
mCarrierPhaseUncertainty = measurement.mCarrierPhaseUncertainty;
mLossOfLock = measurement.mLossOfLock;
- mHasBitNumber = measurement.mHasBitNumber;
mBitNumber = measurement.mBitNumber;
- mHasTimeFromLastBitInNs = measurement.mHasTimeFromLastBitInNs;
- mTimeFromLastBitInNs = measurement.mTimeFromLastBitInNs;
- mHasDopplerShiftInHz = measurement.mHasDopplerShiftInHz;
+ mTimeFromLastBitInMs = measurement.mTimeFromLastBitInMs;
mDopplerShiftInHz = measurement.mDopplerShiftInHz;
- mHasDopplerShiftUncertaintyInHz = measurement.mHasDopplerShiftUncertaintyInHz;
mDopplerShiftUncertaintyInHz = measurement.mDopplerShiftUncertaintyInHz;
mMultipathIndicator = measurement.mMultipathIndicator;
- mHasSnrInDb = measurement.mHasSnrInDb;
mSnrInDb = measurement.mSnrInDb;
- mHasElevationInDeg = measurement.mHasElevationInDeg;
mElevationInDeg = measurement.mElevationInDeg;
- mHasElevationUncertaintyInDeg = measurement.mHasElevationUncertaintyInDeg;
mElevationUncertaintyInDeg = measurement.mElevationUncertaintyInDeg;
- mHasAzimuthInDeg = measurement.mHasAzimuthInDeg;
mAzimuthInDeg = measurement.mAzimuthInDeg;
- mHasAzimuthUncertaintyInDeg = measurement.mHasAzimuthUncertaintyInDeg;
mAzimuthUncertaintyInDeg = measurement.mAzimuthUncertaintyInDeg;
mUsedInFix = measurement.mUsedInFix;
}
@@ -173,35 +207,7 @@
* Resets all the contents to its original state.
*/
public void reset() {
- mPrn = Byte.MIN_VALUE;
- mLocalTimeInNs = Long.MIN_VALUE;
- mReceivedGpsTowInNs = Long.MIN_VALUE;
- mCn0InDbHz = Double.MIN_VALUE;
- mPseudorangeRateInMetersPerSec = Double.MIN_VALUE;
- mPseudorangeRateUncertaintyInMetersPerSec = Double.MIN_VALUE;
- mAccumulatedDeltaRangeInMeters = Double.MIN_VALUE;
- mAccumulatedDeltaRangeUncertaintyInMeters = Double.MIN_VALUE;
-
- resetPseudorangeInMeters();
- resetPseudorangeUncertaintyInMeters();
- resetCodePhaseInChips();
- resetCodePhaseUncertaintyInChips();
- resetCarrierFrequencyInHz();
- resetCarrierCycles();
- resetCarrierPhase();
- resetCarrierPhaseUncertainty();
- setLossOfLock(LOSS_OF_LOCK_UNKNOWN);
- resetBitNumber();
- resetTimeFromLastBitInNs();
- resetDopplerShiftInHz();
- resetDopplerShiftUncertaintyInHz();
- setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN);
- resetSnrInDb();
- resetElevationInDeg();
- resetElevationUncertaintyInDeg();
- resetAzimuthInDeg();
- resetAzimuthUncertaintyInDeg();
- setUsedInFix(false);
+ initialize();
}
/**
@@ -220,22 +226,87 @@
}
/**
- * Gets the local (hardware) time at which the measurement was taken in nanoseconds.
+ * Gets the time offset at which the measurement was taken in nanoseconds.
+ * The reference receiver's time is specified by {@link GpsClock#getTimeInNs()} and should be
+ * interpreted in the same way as indicated by {@link GpsClock#getType()}.
+ *
+ * The sign of this value is given by the following equation:
+ * measurement time = time_ns + time_offset_ns
+ *
+ * The value provides an individual time-stamp for the measurement, and allows sub-nanosecond
+ * accuracy.
*/
- public long getLocalTimeInNs() {
- return mLocalTimeInNs;
+ public double getTimeOffsetInNs() {
+ return mTimeOffsetInNs;
}
/**
- * Sets the measurement's local (hardware) time in nanoseconds.
+ * Sets the time offset at which the measurement was taken in nanoseconds.
*/
- public void setLocalTimeInNs(long value) {
- mLocalTimeInNs = value;
+ public void setTimeOffsetInNs(double value) {
+ mTimeOffsetInNs = value;
}
/**
- * Gets the received GPS Time-of-Week in nanoseconds.
+ * Gets per-satellite sync state.
+ * It represents the current sync state for the associated satellite.
+ *
+ * This value helps interpret {@link #getReceivedGpsTowInNs()}.
+ */
+ public short getState() {
+ return mState;
+ }
+
+ /**
+ * Sets the sync state.
+ */
+ public void setState(short value) {
+ switch (value) {
+ case STATE_UNKNOWN:
+ case STATE_BIT_SYNC:
+ case STATE_CODE_LOCK:
+ case STATE_SUBFRAME_SYNC:
+ case STATE_TOW_DECODED:
+ mState = value;
+ break;
+ default:
+ Log.d(TAG, "Sanitizing invalid 'sync state': " + value);
+ mState = STATE_UNKNOWN;
+ break;
+ }
+ }
+
+ /**
+ * Gets a string representation of the 'sync state'.
+ * For internal and logging use only.
+ */
+ private String getStateString() {
+ switch (mState) {
+ case STATE_UNKNOWN:
+ return "Unknown";
+ case STATE_BIT_SYNC:
+ return "BitSync";
+ case STATE_CODE_LOCK:
+ return "CodeLock";
+ case STATE_SUBFRAME_SYNC:
+ return "SubframeSync";
+ case STATE_TOW_DECODED:
+ return "TowDecoded";
+ default:
+ return "<Invalid>";
+ }
+ }
+
+ /**
+ * Gets the received GPS Time-of-Week at the measurement time, in nanoseconds.
* The value is relative to the beginning of the current GPS week.
+ *
+ * Given {@link #getState()} of the GPS receiver, the range of this field can be:
+ * Searching : [ 0 ] : {@link #STATE_UNKNOWN} is set
+ * Ranging code lock : [ 0 1 ms ] : {@link #STATE_CODE_LOCK} is set
+ * Bit sync : [ 0 20 ms ] : {@link #STATE_BIT_SYNC} is set
+ * Subframe sync : [ 0 6 ms ] : {@link #STATE_SUBFRAME_SYNC} is set
+ * TOW decoded : [ 0 1 week ] : {@link #STATE_TOW_DECODED} is set
*/
public long getReceivedGpsTowInNs() {
return mReceivedGpsTowInNs;
@@ -249,6 +320,20 @@
}
/**
+ * Gets the received GPS time-of-week's uncertainty (1-Sigma) in nanoseconds.
+ */
+ public long getReceivedGpsTowUncertaintyInNs() {
+ return mReceivedGpsTowUncertaintyInNs;
+ }
+
+ /**
+ * Sets the received GPS time-of-week's uncertainty (1-Sigma) in nanoseconds.
+ */
+ public void setReceivedGpsTowUncertaintyInNs(long value) {
+ mReceivedGpsTowUncertaintyInNs = value;
+ }
+
+ /**
* Gets the Carrier-to-noise density in dB-Hz.
* Range: [0, 63].
*
@@ -296,8 +381,56 @@
}
/**
+ * Gets 'Accumulated Delta Range' state.
+ * It indicates whether {@link #getAccumulatedDeltaRangeInMeters()} is reset or there is a
+ * cycle slip (indicating 'loss of lock').
+ */
+ public short getAccumulatedDeltaRangeState() {
+ return mAccumulatedDeltaRangeState;
+ }
+
+ /**
+ * Sets the 'Accumulated Delta Range' state.
+ */
+ public void setAccumulatedDeltaRangeState(short value) {
+ switch (value) {
+ case ADR_STATE_UNKNOWN:
+ case ADR_STATE_VALID:
+ case ADR_STATE_RESET:
+ case ADR_STATE_CYCLE_SLIP:
+ mAccumulatedDeltaRangeState = value;
+ break;
+ default:
+ Log.d(TAG, "Sanitizing invalid 'Accumulated Delta Range state': " + value);
+ mAccumulatedDeltaRangeState = ADR_STATE_UNKNOWN;
+ break;
+ }
+ }
+
+ /**
+ * Gets a string representation of the 'Accumulated Delta Range state'.
+ * For internal and logging use only.
+ */
+ private String getAccumulatedDeltaRangeStateString() {
+ switch (mAccumulatedDeltaRangeState) {
+ case ADR_STATE_UNKNOWN:
+ return "Unknown";
+ case ADR_STATE_VALID:
+ return "Valid";
+ case ADR_STATE_RESET:
+ return "Reset";
+ case ADR_STATE_CYCLE_SLIP:
+ return "CycleSlip";
+ default:
+ return "<Invalid>";
+ }
+ }
+
+ /**
* Gets the accumulated delta range since the last channel reset, in meters.
* The reported value includes {@link #getAccumulatedDeltaRangeUncertaintyInMeters()}.
+ *
+ * The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
*/
public double getAccumulatedDeltaRangeInMeters() {
return mAccumulatedDeltaRangeInMeters;
@@ -320,6 +453,8 @@
/**
* Sets the accumulated delta range's uncertainty (1-sigma) in meters.
+ *
+ * The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
*/
public void setAccumulatedDeltaRangeUncertaintyInMeters(double value) {
mAccumulatedDeltaRangeUncertaintyInMeters = value;
@@ -329,7 +464,7 @@
* Returns true if {@link #getPseudorangeInMeters()} is available, false otherwise.
*/
public boolean hasPseudorangeInMeters() {
- return mHasPseudorangeInMeters;
+ return isFlagSet(HAS_PSEUDORANGE);
}
/**
@@ -346,7 +481,7 @@
* Sets the Pseudo-range in meters.
*/
public void setPseudorangeInMeters(double value) {
- mHasPseudorangeInMeters = true;
+ setFlag(HAS_PSEUDORANGE);
mPseudorangeInMeters = value;
}
@@ -354,7 +489,7 @@
* Resets the Pseudo-range in meters.
*/
public void resetPseudorangeInMeters() {
- mHasPseudorangeInMeters = false;
+ resetFlag(HAS_PSEUDORANGE);
mPseudorangeInMeters = Double.NaN;
}
@@ -362,7 +497,7 @@
* Returns true if {@link #getPseudorangeUncertaintyInMeters()} is available, false otherwise.
*/
public boolean hasPseudorangeUncertaintyInMeters() {
- return mHasPseudorangeUncertaintyInMeters;
+ return isFlagSet(HAS_PSEUDORANGE_UNCERTAINTY);
}
/**
@@ -380,7 +515,7 @@
* Sets the pseudo-range's uncertainty (1-Sigma) in meters.
*/
public void setPseudorangeUncertaintyInMeters(double value) {
- mHasPseudorangeUncertaintyInMeters = true;
+ setFlag(HAS_PSEUDORANGE_UNCERTAINTY);
mPseudorangeUncertaintyInMeters = value;
}
@@ -388,7 +523,7 @@
* Resets the pseudo-range's uncertainty (1-Sigma) in meters.
*/
public void resetPseudorangeUncertaintyInMeters() {
- mHasPseudorangeUncertaintyInMeters = false;
+ resetFlag(HAS_PSEUDORANGE_UNCERTAINTY);
mPseudorangeUncertaintyInMeters = Double.NaN;
}
@@ -396,7 +531,7 @@
* Returns true if {@link #getCodePhaseInChips()} is available, false otherwise.
*/
public boolean hasCodePhaseInChips() {
- return mHasCodePhaseInChips;
+ return isFlagSet(HAS_CODE_PHASE);
}
/**
@@ -415,7 +550,7 @@
* Sets the Code-phase in chips.
*/
public void setCodePhaseInChips(double value) {
- mHasCodePhaseInChips = true;
+ setFlag(HAS_CODE_PHASE);
mCodePhaseInChips = value;
}
@@ -423,7 +558,7 @@
* Resets the Code-phase in chips.
*/
public void resetCodePhaseInChips() {
- mHasCodePhaseInChips = false;
+ resetFlag(HAS_CODE_PHASE);
mCodePhaseInChips = Double.NaN;
}
@@ -431,7 +566,7 @@
* Returns true if {@link #getCodePhaseUncertaintyInChips()} is available, false otherwise.
*/
public boolean hasCodePhaseUncertaintyInChips() {
- return mHasCodePhaseUncertaintyInChips;
+ return isFlagSet(HAS_CODE_PHASE_UNCERTAINTY);
}
/**
@@ -448,7 +583,7 @@
* Sets the Code-phase's uncertainty (1-Sigma) in fractions of chips.
*/
public void setCodePhaseUncertaintyInChips(double value) {
- mHasCodePhaseUncertaintyInChips = true;
+ setFlag(HAS_CODE_PHASE_UNCERTAINTY);
mCodePhaseUncertaintyInChips = value;
}
@@ -456,7 +591,7 @@
* Resets the Code-phase's uncertainty (1-Sigma) in fractions of chips.
*/
public void resetCodePhaseUncertaintyInChips() {
- mHasCodePhaseUncertaintyInChips = false;
+ resetFlag(HAS_CODE_PHASE_UNCERTAINTY);
mCodePhaseUncertaintyInChips = Double.NaN;
}
@@ -464,7 +599,7 @@
* Returns true if {@link #getCarrierFrequencyInHz()} is available, false otherwise.
*/
public boolean hasCarrierFrequencyInHz() {
- return mHasCarrierFrequencyInHz;
+ return isFlagSet(HAS_CARRIER_FREQUENCY);
}
/**
@@ -481,7 +616,7 @@
* Sets the Carrier frequency (L1 or L2) in Hz.
*/
public void setCarrierFrequencyInHz(float carrierFrequencyInHz) {
- mHasCarrierFrequencyInHz = true;
+ setFlag(HAS_CARRIER_FREQUENCY);
mCarrierFrequencyInHz = carrierFrequencyInHz;
}
@@ -489,7 +624,7 @@
* Resets the Carrier frequency (L1 or L2) in Hz.
*/
public void resetCarrierFrequencyInHz() {
- mHasCarrierFrequencyInHz = false;
+ resetFlag(HAS_CARRIER_FREQUENCY);
mCarrierFrequencyInHz = Float.NaN;
}
@@ -497,7 +632,7 @@
* Returns true if {@link #getCarrierCycles()} is available, false otherwise.
*/
public boolean hasCarrierCycles() {
- return mHasCarrierCycles;
+ return isFlagSet(HAS_CARRIER_CYCLES);
}
/**
@@ -514,7 +649,7 @@
* Sets the number of full carrier cycles between the satellite and the receiver.
*/
public void setCarrierCycles(long value) {
- mHasCarrierCycles = true;
+ setFlag(HAS_CARRIER_CYCLES);
mCarrierCycles = value;
}
@@ -522,7 +657,7 @@
* Resets the number of full carrier cycles between the satellite and the receiver.
*/
public void resetCarrierCycles() {
- mHasCarrierCycles = false;
+ resetFlag(HAS_CARRIER_CYCLES);
mCarrierCycles = Long.MIN_VALUE;
}
@@ -530,7 +665,7 @@
* Returns true if {@link #getCarrierPhase()} is available, false otherwise.
*/
public boolean hasCarrierPhase() {
- return mHasCarrierPhase;
+ return isFlagSet(HAS_CARRIER_PHASE);
}
/**
@@ -551,7 +686,7 @@
* Sets the RF phase detected by the receiver.
*/
public void setCarrierPhase(double value) {
- mHasCarrierPhase = true;
+ setFlag(HAS_CARRIER_PHASE);
mCarrierPhase = value;
}
@@ -559,7 +694,7 @@
* Resets the RF phase detected by the receiver.
*/
public void resetCarrierPhase() {
- mHasCarrierPhase = false;
+ resetFlag(HAS_CARRIER_PHASE);
mCarrierPhase = Double.NaN;
}
@@ -567,7 +702,7 @@
* Returns true if {@link #getCarrierPhaseUncertainty()} is available, false otherwise.
*/
public boolean hasCarrierPhaseUncertainty() {
- return mHasCarrierPhaseUncertainty;
+ return isFlagSet(HAS_CARRIER_PHASE_UNCERTAINTY);
}
/**
@@ -584,7 +719,7 @@
* Sets the Carrier-phase's uncertainty (1-Sigma) in cycles.
*/
public void setCarrierPhaseUncertainty(double value) {
- mHasCarrierPhaseUncertainty = true;
+ setFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
mCarrierPhaseUncertainty = value;
}
@@ -592,21 +727,21 @@
* Resets the Carrier-phase's uncertainty (1-Sigma) in cycles.
*/
public void resetCarrierPhaseUncertainty() {
- mHasCarrierPhaseUncertainty = false;
+ resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
mCarrierPhaseUncertainty = Double.NaN;
}
/**
* Gets a value indicating the 'loss of lock' state of the event.
*/
- public short getLossOfLock() {
+ public byte getLossOfLock() {
return mLossOfLock;
}
/**
* Sets the 'loss of lock' status.
*/
- public void setLossOfLock(short value) {
+ public void setLossOfLock(byte value) {
switch (value) {
case LOSS_OF_LOCK_UNKNOWN:
case LOSS_OF_LOCK_OK:
@@ -633,7 +768,7 @@
case LOSS_OF_LOCK_CYCLE_SLIP:
return "CycleSlip";
default:
- return "Invalid";
+ return "<Invalid>";
}
}
@@ -641,7 +776,7 @@
* Returns true if {@link #getBitNumber()} is available, false otherwise.
*/
public boolean hasBitNumber() {
- return mHasBitNumber;
+ return isFlagSet(HAS_BIT_NUMBER);
}
/**
@@ -657,7 +792,7 @@
* Sets the bit number within the broadcast frame.
*/
public void setBitNumber(short bitNumber) {
- mHasBitNumber = true;
+ setFlag(HAS_BIT_NUMBER);
mBitNumber = bitNumber;
}
@@ -665,48 +800,48 @@
* Resets the bit number within the broadcast frame.
*/
public void resetBitNumber() {
- mHasBitNumber = false;
+ resetFlag(HAS_BIT_NUMBER);
mBitNumber = Short.MIN_VALUE;
}
/**
- * Returns true if {@link #getTimeFromLastBitInNs()} is available, false otherwise.
+ * Returns true if {@link #getTimeFromLastBitInMs()} is available, false otherwise.
*/
- public boolean hasTimeFromLastBitInNs() {
- return mHasTimeFromLastBitInNs;
+ public boolean hasTimeFromLastBitInMs() {
+ return isFlagSet(HAS_TIME_FROM_LAST_BIT);
}
/**
- * Gets the elapsed time since the last received bit in nanoseconds.
- * Range: [0, 20000000].
+ * Gets the elapsed time since the last received bit in milliseconds.
+ * Range: [0, 20].
*
- * The value is only available if {@link #hasTimeFromLastBitInNs()} is true.
+ * The value is only available if {@link #hasTimeFromLastBitInMs()} is true.
*/
- public long getTimeFromLastBitInNs() {
- return mTimeFromLastBitInNs;
+ public short getTimeFromLastBitInMs() {
+ return mTimeFromLastBitInMs;
}
/**
- * Sets the elapsed time since the last received bit in nanoseconds.
+ * Sets the elapsed time since the last received bit in milliseconds.
*/
- public void setTimeFromLastBitInNs(long value) {
- mHasTimeFromLastBitInNs = true;
- mTimeFromLastBitInNs = value;
+ public void setTimeFromLastBitInMs(short value) {
+ setFlag(HAS_TIME_FROM_LAST_BIT);
+ mTimeFromLastBitInMs = value;
}
/**
- * Resets the elapsed time since the last received bit in nanoseconds.
+ * Resets the elapsed time since the last received bit in milliseconds.
*/
- public void resetTimeFromLastBitInNs() {
- mHasTimeFromLastBitInNs = false;
- mTimeFromLastBitInNs = Long.MIN_VALUE;
+ public void resetTimeFromLastBitInMs() {
+ resetFlag(HAS_TIME_FROM_LAST_BIT);
+ mTimeFromLastBitInMs = Short.MIN_VALUE;
}
/**
* Returns true if {@link #getDopplerShiftInHz()} is available, false otherwise.
*/
public boolean hasDopplerShiftInHz() {
- return mHasDopplerShiftInHz;
+ return isFlagSet(HAS_DOPPLER_SHIFT);
}
/**
@@ -726,7 +861,7 @@
* Sets the Doppler shift in Hz.
*/
public void setDopplerShiftInHz(double value) {
- mHasDopplerShiftInHz = true;
+ setFlag(HAS_DOPPLER_SHIFT);
mDopplerShiftInHz = value;
}
@@ -734,7 +869,7 @@
* Resets the Doppler shift in Hz.
*/
public void resetDopplerShiftInHz() {
- mHasDopplerShiftInHz = false;
+ resetFlag(HAS_DOPPLER_SHIFT);
mDopplerShiftInHz = Double.NaN;
}
@@ -742,7 +877,7 @@
* Returns true if {@link #getDopplerShiftUncertaintyInHz()} is available, false otherwise.
*/
public boolean hasDopplerShiftUncertaintyInHz() {
- return mHasDopplerShiftUncertaintyInHz;
+ return isFlagSet(HAS_DOPPLER_SHIFT_UNCERTAINTY);
}
/**
@@ -759,7 +894,7 @@
* Sets the Doppler's shift uncertainty (1-Sigma) in Hz.
*/
public void setDopplerShiftUncertaintyInHz(double value) {
- mHasDopplerShiftUncertaintyInHz = true;
+ setFlag(HAS_DOPPLER_SHIFT_UNCERTAINTY);
mDopplerShiftUncertaintyInHz = value;
}
@@ -767,21 +902,21 @@
* Resets the Doppler's shift uncertainty (1-Sigma) in Hz.
*/
public void resetDopplerShiftUncertaintyInHz() {
- mHasDopplerShiftUncertaintyInHz = false;
+ resetFlag(HAS_DOPPLER_SHIFT_UNCERTAINTY);
mDopplerShiftUncertaintyInHz = Double.NaN;
}
/**
* Gets a value indicating the 'multipath' state of the event.
*/
- public short getMultipathIndicator() {
+ public byte getMultipathIndicator() {
return mMultipathIndicator;
}
/**
* Sets the 'multi-path' indicator.
*/
- public void setMultipathIndicator(short value) {
+ public void setMultipathIndicator(byte value) {
switch (value) {
case MULTIPATH_INDICATOR_UNKNOWN:
case MULTIPATH_INDICATOR_DETECTED:
@@ -806,9 +941,9 @@
case MULTIPATH_INDICATOR_DETECTED:
return "Detected";
case MULTIPATH_INDICATOR_NOT_USED:
- return "NotDetected";
+ return "NotUsed";
default:
- return "Invalid";
+ return "<Invalid>";
}
}
@@ -816,7 +951,7 @@
* Returns true if {@link #getSnrInDb()} is available, false otherwise.
*/
public boolean hasSnrInDb() {
- return mHasSnrInDb;
+ return isFlagSet(HAS_SNR);
}
/**
@@ -832,7 +967,7 @@
* Sets the Signal-to-noise ratio (SNR) in dB.
*/
public void setSnrInDb(double snrInDb) {
- mHasSnrInDb = true;
+ setFlag(HAS_SNR);
mSnrInDb = snrInDb;
}
@@ -840,7 +975,7 @@
* Resets the Signal-to-noise ratio (SNR) in dB.
*/
public void resetSnrInDb() {
- mHasSnrInDb = false;
+ resetFlag(HAS_SNR);
mSnrInDb = Double.NaN;
}
@@ -848,7 +983,7 @@
* Returns true if {@link #getElevationInDeg()} is available, false otherwise.
*/
public boolean hasElevationInDeg() {
- return mHasElevationInDeg;
+ return isFlagSet(HAS_ELEVATION);
}
/**
@@ -866,7 +1001,7 @@
* Sets the Elevation in degrees.
*/
public void setElevationInDeg(double elevationInDeg) {
- mHasElevationInDeg = true;
+ setFlag(HAS_ELEVATION);
mElevationInDeg = elevationInDeg;
}
@@ -874,7 +1009,7 @@
* Resets the Elevation in degrees.
*/
public void resetElevationInDeg() {
- mHasElevationInDeg = false;
+ resetFlag(HAS_ELEVATION);
mElevationInDeg = Double.NaN;
}
@@ -882,7 +1017,7 @@
* Returns true if {@link #getElevationUncertaintyInDeg()} is available, false otherwise.
*/
public boolean hasElevationUncertaintyInDeg() {
- return mHasElevationUncertaintyInDeg;
+ return isFlagSet(HAS_ELEVATION_UNCERTAINTY);
}
/**
@@ -901,7 +1036,7 @@
* Sets the elevation's uncertainty (1-Sigma) in degrees.
*/
public void setElevationUncertaintyInDeg(double value) {
- mHasElevationUncertaintyInDeg = true;
+ setFlag(HAS_ELEVATION_UNCERTAINTY);
mElevationUncertaintyInDeg = value;
}
@@ -909,7 +1044,7 @@
* Resets the elevation's uncertainty (1-Sigma) in degrees.
*/
public void resetElevationUncertaintyInDeg() {
- mHasElevationUncertaintyInDeg = false;
+ resetFlag(HAS_ELEVATION_UNCERTAINTY);
mElevationUncertaintyInDeg = Double.NaN;
}
@@ -917,7 +1052,7 @@
* Returns true if {@link #getAzimuthInDeg()} is available, false otherwise.
*/
public boolean hasAzimuthInDeg() {
- return mHasAzimuthInDeg;
+ return isFlagSet(HAS_AZIMUTH);
}
/**
@@ -936,7 +1071,7 @@
* Sets the Azimuth in degrees.
*/
public void setAzimuthInDeg(double value) {
- mHasAzimuthInDeg = true;
+ setFlag(HAS_AZIMUTH);
mAzimuthInDeg = value;
}
@@ -944,7 +1079,7 @@
* Resets the Azimuth in degrees.
*/
public void resetAzimuthInDeg() {
- mHasAzimuthInDeg = false;
+ resetFlag(HAS_AZIMUTH);
mAzimuthInDeg = Double.NaN;
}
@@ -952,7 +1087,7 @@
* Returns true if {@link #getAzimuthUncertaintyInDeg()} is available, false otherwise.
*/
public boolean hasAzimuthUncertaintyInDeg() {
- return mHasAzimuthUncertaintyInDeg;
+ return isFlagSet(HAS_AZIMUTH_UNCERTAINTY);
}
/**
@@ -971,7 +1106,7 @@
* Sets the Azimuth's uncertainty (1-Sigma) in degrees.
*/
public void setAzimuthUncertaintyInDeg(double value) {
- mHasAzimuthUncertaintyInDeg = true;
+ setFlag(HAS_AZIMUTH_UNCERTAINTY);
mAzimuthUncertaintyInDeg = value;
}
@@ -979,7 +1114,7 @@
* Resets the Azimuth's uncertainty (1-Sigma) in degrees.
*/
public void resetAzimuthUncertaintyInDeg() {
- mHasAzimuthUncertaintyInDeg = false;
+ resetFlag(HAS_AZIMUTH_UNCERTAINTY);
mAzimuthUncertaintyInDeg = Double.NaN;
}
@@ -989,7 +1124,7 @@
*
* @return A non-null value if the data is available, null otherwise.
*/
- public Boolean isUsedInFix() {
+ public boolean isUsedInFix() {
return mUsedInFix;
}
@@ -1005,50 +1140,36 @@
public GpsMeasurement createFromParcel(Parcel parcel) {
GpsMeasurement gpsMeasurement = new GpsMeasurement();
+ gpsMeasurement.mFlags = parcel.readInt();
gpsMeasurement.mPrn = parcel.readByte();
- gpsMeasurement.mLocalTimeInNs = parcel.readLong();
+ gpsMeasurement.mTimeOffsetInNs = parcel.readDouble();
+ gpsMeasurement.mState = (short) parcel.readInt();
gpsMeasurement.mReceivedGpsTowInNs = parcel.readLong();
+ gpsMeasurement.mReceivedGpsTowUncertaintyInNs = parcel.readLong();
gpsMeasurement.mCn0InDbHz = parcel.readDouble();
gpsMeasurement.mPseudorangeRateInMetersPerSec = parcel.readDouble();
gpsMeasurement.mPseudorangeRateUncertaintyInMetersPerSec = parcel.readDouble();
+ gpsMeasurement.mAccumulatedDeltaRangeState = (short) parcel.readInt();
gpsMeasurement.mAccumulatedDeltaRangeInMeters = parcel.readDouble();
gpsMeasurement.mAccumulatedDeltaRangeUncertaintyInMeters = parcel.readDouble();
-
- gpsMeasurement.mHasPseudorangeInMeters = parcel.readInt() != 0;
gpsMeasurement.mPseudorangeInMeters = parcel.readDouble();
- gpsMeasurement.mHasPseudorangeUncertaintyInMeters = parcel.readInt() != 0;
gpsMeasurement.mPseudorangeUncertaintyInMeters = parcel.readDouble();
- gpsMeasurement.mHasCodePhaseInChips = parcel.readInt() != 0;
gpsMeasurement.mCodePhaseInChips = parcel.readDouble();
- gpsMeasurement.mHasCodePhaseUncertaintyInChips = parcel.readInt() != 0;
gpsMeasurement.mCodePhaseUncertaintyInChips = parcel.readDouble();
- gpsMeasurement.mHasCarrierFrequencyInHz = parcel.readInt() != 0;
gpsMeasurement.mCarrierFrequencyInHz = parcel.readFloat();
- gpsMeasurement.mHasCarrierCycles = parcel.readInt() != 0;
gpsMeasurement.mCarrierCycles = parcel.readLong();
- gpsMeasurement.mHasCarrierPhase = parcel.readInt() != 0;
gpsMeasurement.mCarrierPhase = parcel.readDouble();
- gpsMeasurement.mHasCarrierPhaseUncertainty = parcel.readInt() != 0;
gpsMeasurement.mCarrierPhaseUncertainty = parcel.readDouble();
- gpsMeasurement.mLossOfLock = (short) parcel.readInt();
- gpsMeasurement.mHasBitNumber = parcel.readInt() != 0;
+ gpsMeasurement.mLossOfLock = parcel.readByte();
gpsMeasurement.mBitNumber = (short) parcel.readInt();
- gpsMeasurement.mHasTimeFromLastBitInNs = parcel.readInt() != 0;
- gpsMeasurement.mTimeFromLastBitInNs = parcel.readLong();
- gpsMeasurement.mHasDopplerShiftInHz = parcel.readInt() != 0;
+ gpsMeasurement.mTimeFromLastBitInMs = (short) parcel.readInt();
gpsMeasurement.mDopplerShiftInHz = parcel.readDouble();
- gpsMeasurement.mHasDopplerShiftUncertaintyInHz = parcel.readInt() != 0;
gpsMeasurement.mDopplerShiftUncertaintyInHz = parcel.readDouble();
- gpsMeasurement.mMultipathIndicator = (short) parcel.readInt();
- gpsMeasurement.mHasSnrInDb = parcel.readInt() != 0;
+ gpsMeasurement.mMultipathIndicator = parcel.readByte();
gpsMeasurement.mSnrInDb = parcel.readDouble();
- gpsMeasurement.mHasElevationInDeg = parcel.readInt() != 0;
gpsMeasurement.mElevationInDeg = parcel.readDouble();
- gpsMeasurement.mHasElevationUncertaintyInDeg = parcel.readInt() != 0;
gpsMeasurement.mElevationUncertaintyInDeg = parcel.readDouble();
- gpsMeasurement.mHasAzimuthInDeg = parcel.readInt() != 0;
gpsMeasurement.mAzimuthInDeg = parcel.readDouble();
- gpsMeasurement.mHasAzimuthUncertaintyInDeg = parcel.readInt() != 0;
gpsMeasurement.mAzimuthUncertaintyInDeg = parcel.readDouble();
gpsMeasurement.mUsedInFix = parcel.readInt() != 0;
@@ -1062,50 +1183,36 @@
};
public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mFlags);
parcel.writeByte(mPrn);
- parcel.writeLong(mLocalTimeInNs);
+ parcel.writeDouble(mTimeOffsetInNs);
+ parcel.writeInt(mState);
parcel.writeLong(mReceivedGpsTowInNs);
+ parcel.writeLong(mReceivedGpsTowUncertaintyInNs);
parcel.writeDouble(mCn0InDbHz);
parcel.writeDouble(mPseudorangeRateInMetersPerSec);
parcel.writeDouble(mPseudorangeRateUncertaintyInMetersPerSec);
+ parcel.writeInt(mAccumulatedDeltaRangeState);
parcel.writeDouble(mAccumulatedDeltaRangeInMeters);
parcel.writeDouble(mAccumulatedDeltaRangeUncertaintyInMeters);
-
- parcel.writeInt(mHasPseudorangeInMeters ? 1 : 0);
parcel.writeDouble(mPseudorangeInMeters);
- parcel.writeInt(mHasPseudorangeUncertaintyInMeters ? 1 : 0);
parcel.writeDouble(mPseudorangeUncertaintyInMeters);
- parcel.writeInt(mHasCodePhaseInChips ? 1 : 0);
parcel.writeDouble(mCodePhaseInChips);
- parcel.writeInt(mHasCodePhaseUncertaintyInChips ? 1 : 0);
parcel.writeDouble(mCodePhaseUncertaintyInChips);
- parcel.writeInt(mHasCarrierFrequencyInHz ? 1 : 0);
parcel.writeFloat(mCarrierFrequencyInHz);
- parcel.writeInt(mHasCarrierCycles ? 1 : 0);
parcel.writeLong(mCarrierCycles);
- parcel.writeInt(mHasCarrierPhase ? 1 : 0);
parcel.writeDouble(mCarrierPhase);
- parcel.writeInt(mHasCarrierPhaseUncertainty ? 1 : 0);
parcel.writeDouble(mCarrierPhaseUncertainty);
- parcel.writeInt(mLossOfLock);
- parcel.writeInt(mHasBitNumber ? 1 : 0);
+ parcel.writeByte(mLossOfLock);
parcel.writeInt(mBitNumber);
- parcel.writeInt(mHasTimeFromLastBitInNs ? 1 : 0);
- parcel.writeLong(mTimeFromLastBitInNs);
- parcel.writeInt(mHasDopplerShiftInHz ? 1 : 0);
+ parcel.writeInt(mTimeFromLastBitInMs);
parcel.writeDouble(mDopplerShiftInHz);
- parcel.writeInt(mHasDopplerShiftUncertaintyInHz ? 1 : 0);
parcel.writeDouble(mDopplerShiftUncertaintyInHz);
- parcel.writeInt(mMultipathIndicator);
- parcel.writeInt(mHasSnrInDb ? 1 : 0);
+ parcel.writeByte(mMultipathIndicator);
parcel.writeDouble(mSnrInDb);
- parcel.writeInt(mHasElevationInDeg ? 1 : 0);
parcel.writeDouble(mElevationInDeg);
- parcel.writeInt(mHasElevationUncertaintyInDeg ? 1 : 0);
parcel.writeDouble(mElevationUncertaintyInDeg);
- parcel.writeInt(mHasAzimuthInDeg ? 1 : 0);
parcel.writeDouble(mAzimuthInDeg);
- parcel.writeInt(mHasAzimuthUncertaintyInDeg ? 1 : 0);
parcel.writeDouble(mAzimuthUncertaintyInDeg);
parcel.writeInt(mUsedInFix ? 1 : 0);
}
@@ -1123,9 +1230,16 @@
builder.append(String.format(format, "Prn", mPrn));
- builder.append(String.format(format, "LocalTimeInNs", mLocalTimeInNs));
+ builder.append(String.format(format, "TimeOffsetInNs", mTimeOffsetInNs));
- builder.append(String.format(format, "ReceivedGpsTowInNs", mReceivedGpsTowInNs));
+ builder.append(String.format(format, "State", getStateString()));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "ReceivedGpsTowInNs",
+ mReceivedGpsTowInNs,
+ "ReceivedGpsTowUncertaintyInNs",
+ mReceivedGpsTowUncertaintyInNs));
builder.append(String.format(format, "Cn0InDbHz", mCn0InDbHz));
@@ -1137,86 +1251,137 @@
mPseudorangeRateUncertaintyInMetersPerSec));
builder.append(String.format(
+ format,
+ "AccumulatedDeltaRangeState",
+ getAccumulatedDeltaRangeStateString()));
+
+ builder.append(String.format(
formatWithUncertainty,
"AccumulatedDeltaRangeInMeters",
mAccumulatedDeltaRangeInMeters,
"AccumulatedDeltaRangeUncertaintyInMeters",
mAccumulatedDeltaRangeUncertaintyInMeters));
-
builder.append(String.format(
formatWithUncertainty,
"PseudorangeInMeters",
- mHasPseudorangeInMeters ? mPseudorangeInMeters : null,
+ hasPseudorangeInMeters() ? mPseudorangeInMeters : null,
"PseudorangeUncertaintyInMeters",
- mHasPseudorangeUncertaintyInMeters ? mPseudorangeUncertaintyInMeters : null));
+ hasPseudorangeUncertaintyInMeters() ? mPseudorangeUncertaintyInMeters : null));
builder.append(String.format(
formatWithUncertainty,
"CodePhaseInChips",
- mHasCodePhaseInChips ? mCodePhaseInChips : null,
+ hasCodePhaseInChips() ? mCodePhaseInChips : null,
"CodePhaseUncertaintyInChips",
- mHasCodePhaseUncertaintyInChips ? mCodePhaseUncertaintyInChips : null));
+ hasCodePhaseUncertaintyInChips() ? mCodePhaseUncertaintyInChips : null));
builder.append(String.format(
format,
"CarrierFrequencyInHz",
- mHasCarrierFrequencyInHz ? mCarrierFrequencyInHz : null));
+ hasCarrierFrequencyInHz() ? mCarrierFrequencyInHz : null));
builder.append(String.format(
format,
"CarrierCycles",
- mHasCarrierCycles ? mCarrierCycles : null));
+ hasCarrierCycles() ? mCarrierCycles : null));
builder.append(String.format(
formatWithUncertainty,
"CarrierPhase",
- mHasCarrierPhase ? mCarrierPhase : null,
+ hasCarrierPhase() ? mCarrierPhase : null,
"CarrierPhaseUncertainty",
- mHasCarrierPhaseUncertainty ? mCarrierPhaseUncertainty : null));
+ hasCarrierPhaseUncertainty() ? mCarrierPhaseUncertainty : null));
builder.append(String.format(format, "LossOfLock", getLossOfLockString()));
builder.append(String.format(
format,
"BitNumber",
- mHasBitNumber ? mBitNumber : null));
+ hasBitNumber() ? mBitNumber : null));
builder.append(String.format(
format,
- "TimeFromLastBitInNs",
- mHasTimeFromLastBitInNs ? mTimeFromLastBitInNs : null));
+ "TimeFromLastBitInMs",
+ hasTimeFromLastBitInMs() ? mTimeFromLastBitInMs : null));
builder.append(String.format(
formatWithUncertainty,
"DopplerShiftInHz",
- mHasDopplerShiftInHz ? mDopplerShiftInHz : null,
+ hasDopplerShiftInHz() ? mDopplerShiftInHz : null,
"DopplerShiftUncertaintyInHz",
- mHasDopplerShiftUncertaintyInHz ? mDopplerShiftUncertaintyInHz : null));
+ hasDopplerShiftUncertaintyInHz() ? mDopplerShiftUncertaintyInHz : null));
builder.append(String.format(format, "MultipathIndicator", getMultipathIndicatorString()));
builder.append(String.format(
format,
"SnrInDb",
- mHasSnrInDb ? mSnrInDb : null));
+ hasSnrInDb() ? mSnrInDb : null));
builder.append(String.format(
formatWithUncertainty,
"ElevationInDeg",
- mHasElevationInDeg ? mElevationInDeg : null,
+ hasElevationInDeg() ? mElevationInDeg : null,
"ElevationUncertaintyInDeg",
- mHasElevationUncertaintyInDeg ? mElevationUncertaintyInDeg : null));
+ hasElevationUncertaintyInDeg() ? mElevationUncertaintyInDeg : null));
builder.append(String.format(
formatWithUncertainty,
"AzimuthInDeg",
- mHasAzimuthInDeg ? mAzimuthInDeg : null,
+ hasAzimuthInDeg() ? mAzimuthInDeg : null,
"AzimuthUncertaintyInDeg",
- mHasAzimuthUncertaintyInDeg ? mAzimuthUncertaintyInDeg : null));
+ hasAzimuthUncertaintyInDeg() ? mAzimuthUncertaintyInDeg : null));
builder.append(String.format(format, "UsedInFix", mUsedInFix));
return builder.toString();
}
+
+ private void initialize() {
+ mFlags = HAS_NO_FLAGS;
+ setPrn(Byte.MIN_VALUE);
+ setTimeOffsetInNs(Long.MIN_VALUE);
+ setState(STATE_UNKNOWN);
+ setReceivedGpsTowInNs(Long.MIN_VALUE);
+ setReceivedGpsTowUncertaintyInNs(Long.MAX_VALUE);
+ setCn0InDbHz(Double.MIN_VALUE);
+ setPseudorangeRateInMetersPerSec(Double.MIN_VALUE);
+ setPseudorangeRateUncertaintyInMetersPerSec(Double.MIN_VALUE);
+ setAccumulatedDeltaRangeState(ADR_STATE_UNKNOWN);
+ setAccumulatedDeltaRangeInMeters(Double.MIN_VALUE);
+ setAccumulatedDeltaRangeUncertaintyInMeters(Double.MIN_VALUE);
+ resetPseudorangeInMeters();
+ resetPseudorangeUncertaintyInMeters();
+ resetCodePhaseInChips();
+ resetCodePhaseUncertaintyInChips();
+ resetCarrierFrequencyInHz();
+ resetCarrierCycles();
+ resetCarrierPhase();
+ resetCarrierPhaseUncertainty();
+ setLossOfLock(LOSS_OF_LOCK_UNKNOWN);
+ resetBitNumber();
+ resetTimeFromLastBitInMs();
+ resetDopplerShiftInHz();
+ resetDopplerShiftUncertaintyInHz();
+ setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN);
+ resetSnrInDb();
+ resetElevationInDeg();
+ resetElevationUncertaintyInDeg();
+ resetAzimuthInDeg();
+ resetAzimuthUncertaintyInDeg();
+ setUsedInFix(false);
+ }
+
+ private void setFlag(int flag) {
+ mFlags |= flag;
+ }
+
+ private void resetFlag(int flag) {
+ mFlags &= ~flag;
+ }
+
+ private boolean isFlagSet(int flag) {
+ return (mFlags & flag) == flag;
+ }
}
diff --git a/location/java/android/location/GpsMeasurementListenerTransport.java b/location/java/android/location/GpsMeasurementListenerTransport.java
index 48a4b44..2d9a372 100644
--- a/location/java/android/location/GpsMeasurementListenerTransport.java
+++ b/location/java/android/location/GpsMeasurementListenerTransport.java
@@ -16,99 +16,51 @@
package android.location;
-import com.android.internal.util.Preconditions;
-
-import android.annotation.NonNull;
import android.content.Context;
import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
/**
- * A handler class to manage transport listeners for {@link GpsMeasurementsEvent.Listener},
- * and post the events in a handler.
+ * A handler class to manage transport listeners for {@link GpsMeasurementsEvent.Listener}.
*
* @hide
*/
-class GpsMeasurementListenerTransport {
- private static final String TAG = "GpsMeasurementListenerTransport";
-
+class GpsMeasurementListenerTransport
+ extends LocalListenerHelper<GpsMeasurementsEvent.Listener> {
private final Context mContext;
private final ILocationManager mLocationManager;
private final IGpsMeasurementsListener mListenerTransport = new ListenerTransport();
- private final HashSet<GpsMeasurementsEvent.Listener> mListeners =
- new HashSet<GpsMeasurementsEvent.Listener>();
public GpsMeasurementListenerTransport(Context context, ILocationManager locationManager) {
+ super("GpsMeasurementListenerTransport");
mContext = context;
mLocationManager = locationManager;
}
- public boolean add(@NonNull GpsMeasurementsEvent.Listener listener) {
- Preconditions.checkNotNull(listener);
-
- synchronized (mListeners) {
- // we need to register with the service first, because we need to find out if the
- // service will actually support the request before we attempt anything
- if (mListeners.isEmpty()) {
- boolean registeredWithServer;
- try {
- registeredWithServer = mLocationManager.addGpsMeasurementsListener(
- mListenerTransport,
- mContext.getPackageName());
- } catch (RemoteException e) {
- Log.e(TAG, "Error handling first listener.", e);
- return false;
- }
-
- if (!registeredWithServer) {
- Log.e(TAG, "Unable to register listener transport.");
- return false;
- }
- }
-
- if (mListeners.contains(listener)) {
- return true;
- }
-
- mListeners.add(listener);
- }
-
- return true;
+ @Override
+ protected boolean registerWithServer() throws RemoteException {
+ return mLocationManager.addGpsMeasurementsListener(
+ mListenerTransport,
+ mContext.getPackageName());
}
- public void remove(@NonNull GpsMeasurementsEvent.Listener listener) {
- Preconditions.checkNotNull(listener);
-
- synchronized (mListeners) {
- boolean removed = mListeners.remove(listener);
-
- boolean isLastListener = removed && mListeners.isEmpty();
- if (isLastListener) {
- try {
- mLocationManager.removeGpsMeasurementsListener(mListenerTransport);
- } catch (RemoteException e) {
- Log.e(TAG, "Error handling last listener.", e);
- }
- }
- }
+ @Override
+ protected void unregisterFromServer() throws RemoteException {
+ mLocationManager.removeGpsMeasurementsListener(mListenerTransport);
}
private class ListenerTransport extends IGpsMeasurementsListener.Stub {
@Override
- public void onGpsMeasurementsReceived(final GpsMeasurementsEvent eventArgs) {
- Collection<GpsMeasurementsEvent.Listener> listeners;
- synchronized (mListeners) {
- listeners = new ArrayList<GpsMeasurementsEvent.Listener>(mListeners);
- }
+ public void onGpsMeasurementsReceived(final GpsMeasurementsEvent event) {
+ ListenerOperation<GpsMeasurementsEvent.Listener> operation =
+ new ListenerOperation<GpsMeasurementsEvent.Listener>() {
+ @Override
+ public void execute(GpsMeasurementsEvent.Listener listener) throws RemoteException {
+ listener.onGpsMeasurementsReceived(event);
+ }
+ };
- for (final GpsMeasurementsEvent.Listener listener : listeners) {
- listener.onGpsMeasurementsReceived(eventArgs);
- }
+ foreach(operation);
}
}
}
diff --git a/location/java/android/location/GpsNavigationMessage.java b/location/java/android/location/GpsNavigationMessage.java
new file mode 100644
index 0000000..2eb4708
--- /dev/null
+++ b/location/java/android/location/GpsNavigationMessage.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.security.InvalidParameterException;
+
+/**
+ * A class containing a GPS satellite Navigation Message.
+ *
+ * @hide
+ */
+public class GpsNavigationMessage implements Parcelable {
+ private static final String TAG = "GpsNavigationMessage";
+ private static final byte[] EMPTY_ARRAY = new byte[0];
+
+ // The following enumerations must be in sync with the values declared in gps.h
+
+ /**
+ * The type of the navigation message is not available or unknown.
+ */
+ public static final byte TYPE_UNKNOWN = 0;
+
+ /**
+ * The Navigation Message is of type L1 C/A.
+ */
+ public static final byte TYPE_L1CA = 1;
+
+ /**
+ * The Navigation Message is of type L1-CNAV.
+ */
+ public static final byte TYPE_L2CNAV = 2;
+
+ /**
+ * The Navigation Message is of type L5-CNAV.
+ */
+ public static final byte TYPE_L5CNAV = 3;
+
+ /**
+ * The Navigation Message is of type CNAV-2.
+ */
+ public static final byte TYPE_CNAV2 = 4;
+
+ // End enumerations in sync with gps.h
+
+ private byte mType;
+ private byte mPrn;
+ private short mMessageId;
+ private short mSubmessageId;
+ private byte[] mData;
+
+ GpsNavigationMessage() {
+ initialize();
+ }
+
+ /**
+ * Sets all contents to the values stored in the provided object.
+ */
+ public void set(GpsNavigationMessage navigationMessage) {
+ mType = navigationMessage.mType;
+ mPrn = navigationMessage.mPrn;
+ mMessageId = navigationMessage.mMessageId;
+ mSubmessageId = navigationMessage.mSubmessageId;
+ mData = navigationMessage.mData;
+ }
+
+ /**
+ * Resets all the contents to its original state.
+ */
+ public void reset() {
+ initialize();
+ }
+
+ /**
+ * Gets the type of the navigation message contained in the object.
+ */
+ public byte getType() {
+ return mType;
+ }
+
+ /**
+ * Sets the type of the navigation message.
+ */
+ public void setType(byte value) {
+ switch (value) {
+ case TYPE_UNKNOWN:
+ case TYPE_L1CA:
+ case TYPE_L2CNAV:
+ case TYPE_L5CNAV:
+ case TYPE_CNAV2:
+ mType = value;
+ break;
+ default:
+ Log.d(TAG, "Sanitizing invalid 'type': " + value);
+ mType = TYPE_UNKNOWN;
+ break;
+ }
+ }
+
+ /**
+ * Gets a string representation of the 'type'.
+ * For internal and logging use only.
+ */
+ private String getTypeString() {
+ switch (mType) {
+ case TYPE_UNKNOWN:
+ return "Unknown";
+ case TYPE_L1CA:
+ return "L1 C/A";
+ case TYPE_L2CNAV:
+ return "L2-CNAV";
+ case TYPE_L5CNAV:
+ return "L5-CNAV";
+ case TYPE_CNAV2:
+ return "CNAV-2";
+ default:
+ return "<Invalid>";
+ }
+ }
+
+ /**
+ * Gets the Pseudo-random number.
+ * Range: [1, 32].
+ */
+ public byte getPrn() {
+ return mPrn;
+ }
+
+ /**
+ * Sets the Pseud-random number.
+ */
+ public void setPrn(byte value) {
+ mPrn = value;
+ }
+
+ /**
+ * Gets the Message Identifier.
+ * It provides an index so the complete Navigation Message can be assembled. i.e. for L1 C/A
+ * subframe 4 and 5, this value corresponds to the 'frame id' of the navigation message.
+ * Subframe 1, 2, 3 does not contain a 'frame id' and this might be reported as -1.
+ */
+ public short getMessageId() {
+ return mMessageId;
+ }
+
+ /**
+ * Sets the Message Identifier.
+ */
+ public void setMessageId(short value) {
+ mMessageId = value;
+ }
+
+ /**
+ * Gets the Sub-message Identifier.
+ * If required by {@link #getType()}, this value contains a sub-index within the current message
+ * (or frame) that is being transmitted. i.e. for L1 C/A the sub-message identifier corresponds
+ * to the sub-frame Id of the navigation message.
+ */
+ public short getSubmessageId() {
+ return mSubmessageId;
+ }
+
+ /**
+ * Sets the Sub-message identifier.
+ */
+ public void setSubmessageId(short value) {
+ mSubmessageId = value;
+ }
+
+ /**
+ * Gets the data associated with the Navigation Message.
+ * The bytes (or words) specified using big endian format (MSB first).
+ */
+ @NonNull
+ public byte[] getData() {
+ return mData;
+ }
+
+ /**
+ * Sets the data associated with the Navigation Message.
+ */
+ public void setData(byte[] value) {
+ if (value == null) {
+ throw new InvalidParameterException("Data must be a non-null array");
+ }
+
+ mData = value;
+ }
+
+ public static final Creator<GpsNavigationMessage> CREATOR =
+ new Creator<GpsNavigationMessage>() {
+ @Override
+ public GpsNavigationMessage createFromParcel(Parcel parcel) {
+ GpsNavigationMessage navigationMessage = new GpsNavigationMessage();
+
+ navigationMessage.setType(parcel.readByte());
+ navigationMessage.setPrn(parcel.readByte());
+ navigationMessage.setMessageId((short) parcel.readInt());
+ navigationMessage.setSubmessageId((short) parcel.readInt());
+
+ int dataLength = parcel.readInt();
+ byte[] data = new byte[dataLength];
+ parcel.readByteArray(data);
+ navigationMessage.setData(data);
+
+ return navigationMessage;
+ }
+
+ @Override
+ public GpsNavigationMessage[] newArray(int size) {
+ return new GpsNavigationMessage[size];
+ }
+ };
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeByte(mType);
+ parcel.writeByte(mPrn);
+ parcel.writeInt(mMessageId);
+ parcel.writeInt(mSubmessageId);
+ parcel.writeInt(mData.length);
+ parcel.writeByteArray(mData);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ final String format = " %-15s = %s\n";
+ StringBuilder builder = new StringBuilder("GpsNavigationMessage:\n");
+
+ builder.append(String.format(format, "Type", getTypeString()));
+ builder.append(String.format(format, "Prn", mPrn));
+ builder.append(String.format(format, "MessageId", mMessageId));
+ builder.append(String.format(format, "SubmessageId", mSubmessageId));
+
+ builder.append(String.format(format, "Data", "{"));
+ String prefix = " ";
+ for(byte value : mData) {
+ builder.append(prefix);
+ builder.append(value);
+ prefix = ", ";
+ }
+ builder.append(" }");
+
+ return builder.toString();
+ }
+
+ private void initialize() {
+ mType = TYPE_UNKNOWN;
+ mPrn = 0;
+ mMessageId = -1;
+ mSubmessageId = -1;
+ mData = EMPTY_ARRAY;
+ }
+}
diff --git a/telecomm/java/android/telecomm/InCallCall.aidl b/location/java/android/location/GpsNavigationMessageEvent.aidl
similarity index 81%
copy from telecomm/java/android/telecomm/InCallCall.aidl
copy to location/java/android/location/GpsNavigationMessageEvent.aidl
index be2cdf8..f84c2f7 100644
--- a/telecomm/java/android/telecomm/InCallCall.aidl
+++ b/location/java/android/location/GpsNavigationMessageEvent.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2014, The Android Open Source Project
+ * Copyright (C) 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.telecomm;
+package android.location;
-parcelable InCallCall;
+parcelable GpsNavigationMessageEvent;
diff --git a/location/java/android/location/GpsNavigationMessageEvent.java b/location/java/android/location/GpsNavigationMessageEvent.java
new file mode 100644
index 0000000..50ffa75
--- /dev/null
+++ b/location/java/android/location/GpsNavigationMessageEvent.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.InvalidParameterException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * A class implementing a container for data associated with a navigation message event.
+ * Events are delivered to registered instances of {@link Listener}.
+ *
+ * @hide
+ */
+public class GpsNavigationMessageEvent implements Parcelable {
+ private final GpsNavigationMessage mNavigationMessage;
+
+ /**
+ * Used for receiving GPS satellite Navigation Messages from the GPS engine.
+ * You can implement this interface and call
+ * {@link LocationManager#addGpsNavigationMessageListener}.
+ *
+ * @hide
+ */
+ public interface Listener {
+ void onGpsNavigationMessageReceived(GpsNavigationMessageEvent event);
+ }
+
+ public GpsNavigationMessageEvent(GpsNavigationMessage message) {
+ if (message == null) {
+ throw new InvalidParameterException("Parameter 'message' must not be null.");
+ }
+ mNavigationMessage = message;
+ }
+
+ @NonNull
+ public GpsNavigationMessage getNavigationMessage() {
+ return mNavigationMessage;
+ }
+
+ public static final Creator<GpsNavigationMessageEvent> CREATOR =
+ new Creator<GpsNavigationMessageEvent>() {
+ @Override
+ public GpsNavigationMessageEvent createFromParcel(Parcel in) {
+ ClassLoader classLoader = getClass().getClassLoader();
+ GpsNavigationMessage navigationMessage = in.readParcelable(classLoader);
+ return new GpsNavigationMessageEvent(navigationMessage);
+ }
+
+ @Override
+ public GpsNavigationMessageEvent[] newArray(int size) {
+ return new GpsNavigationMessageEvent[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeParcelable(mNavigationMessage, flags);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("[ GpsNavigationMessageEvent:\n\n");
+ builder.append(mNavigationMessage.toString());
+ builder.append("\n]");
+ return builder.toString();
+ }
+}
diff --git a/location/java/android/location/GpsNavigationMessageListenerTransport.java b/location/java/android/location/GpsNavigationMessageListenerTransport.java
new file mode 100644
index 0000000..ec4812b
--- /dev/null
+++ b/location/java/android/location/GpsNavigationMessageListenerTransport.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+import android.content.Context;
+import android.os.RemoteException;
+
+/**
+ * A handler class to manage transport listeners for {@link GpsNavigationMessageEvent.Listener}.
+ *
+ * @hide
+ */
+class GpsNavigationMessageListenerTransport
+ extends LocalListenerHelper<GpsNavigationMessageEvent.Listener> {
+ private final Context mContext;
+ private final ILocationManager mLocationManager;
+
+ private final IGpsNavigationMessageListener mListenerTransport = new ListenerTransport();
+
+ public GpsNavigationMessageListenerTransport(
+ Context context,
+ ILocationManager locationManager) {
+ super("GpsNavigationMessageListenerTransport");
+ mContext = context;
+ mLocationManager = locationManager;
+ }
+
+ @Override
+ protected boolean registerWithServer() throws RemoteException {
+ return mLocationManager.addGpsNavigationMessageListener(
+ mListenerTransport,
+ mContext.getPackageName());
+ }
+
+ @Override
+ protected void unregisterFromServer() throws RemoteException {
+ mLocationManager.removeGpsNavigationMessageListener(mListenerTransport);
+ }
+
+ private class ListenerTransport extends IGpsNavigationMessageListener.Stub {
+ @Override
+ public void onGpsNavigationMessageReceived(final GpsNavigationMessageEvent event) {
+ ListenerOperation<GpsNavigationMessageEvent.Listener> operation =
+ new ListenerOperation<GpsNavigationMessageEvent.Listener>() {
+ @Override
+ public void execute(GpsNavigationMessageEvent.Listener listener)
+ throws RemoteException {
+ listener.onGpsNavigationMessageReceived(event);
+ }
+ };
+
+ foreach(operation);
+ }
+ }
+}
diff --git a/telecomm/java/android/telecomm/InCallCall.aidl b/location/java/android/location/IGpsNavigationMessageListener.aidl
similarity index 66%
copy from telecomm/java/android/telecomm/InCallCall.aidl
copy to location/java/android/location/IGpsNavigationMessageListener.aidl
index be2cdf8..18603fe 100644
--- a/telecomm/java/android/telecomm/InCallCall.aidl
+++ b/location/java/android/location/IGpsNavigationMessageListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2014, The Android Open Source Project
+ * Copyright (C) 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,13 @@
* limitations under the License.
*/
-package android.telecomm;
+package android.location;
-parcelable InCallCall;
+import android.location.GpsNavigationMessageEvent;
+
+/**
+ * {@hide}
+ */
+oneway interface IGpsNavigationMessageListener {
+ void onGpsNavigationMessageReceived(in GpsNavigationMessageEvent event);
+}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index a1acaf1..1501710 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -22,6 +22,7 @@
import android.location.GeocoderParams;
import android.location.Geofence;
import android.location.IGpsMeasurementsListener;
+import android.location.IGpsNavigationMessageListener;
import android.location.IGpsStatusListener;
import android.location.ILocationListener;
import android.location.Location;
@@ -63,6 +64,11 @@
boolean addGpsMeasurementsListener(in IGpsMeasurementsListener listener, in String packageName);
boolean removeGpsMeasurementsListener(in IGpsMeasurementsListener listener);
+ boolean addGpsNavigationMessageListener(
+ in IGpsNavigationMessageListener listener,
+ in String packageName);
+ boolean removeGpsNavigationMessageListener(in IGpsNavigationMessageListener listener);
+
// --- deprecated ---
List<String> getAllProviders();
List<String> getProviders(in Criteria criteria, boolean enabledOnly);
diff --git a/location/java/android/location/LocalListenerHelper.java b/location/java/android/location/LocalListenerHelper.java
new file mode 100644
index 0000000..1f3bf67
--- /dev/null
+++ b/location/java/android/location/LocalListenerHelper.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+import com.android.internal.util.Preconditions;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * A base handler class to manage transport and local listeners.
+ *
+ * @hide
+ */
+abstract class LocalListenerHelper<TListener> {
+ private final HashSet<TListener> mListeners = new HashSet<TListener>();
+ private final String mTag;
+
+ protected LocalListenerHelper(String name) {
+ Preconditions.checkNotNull(name);
+ mTag = name;
+ }
+
+ public boolean add(@NonNull TListener listener) {
+ Preconditions.checkNotNull(listener);
+
+ synchronized (mListeners) {
+ // we need to register with the service first, because we need to find out if the
+ // service will actually support the request before we attempt anything
+ if (mListeners.isEmpty()) {
+ boolean registeredWithService;
+ try {
+ registeredWithService = registerWithServer();
+ } catch (RemoteException e) {
+ Log.e(mTag, "Error handling first listener.", e);
+ return false;
+ }
+ if (!registeredWithService) {
+ Log.e(mTag, "Unable to register listener transport.");
+ return false;
+ }
+ }
+
+ if (mListeners.contains(listener)) {
+ return true;
+ }
+ mListeners.add(listener);
+ }
+ return true;
+ }
+
+ public void remove(@NonNull TListener listener) {
+ Preconditions.checkNotNull(listener);
+
+ synchronized (mListeners) {
+ boolean removed = mListeners.remove(listener);
+ boolean isLastRemoved = removed && mListeners.isEmpty();
+ if (isLastRemoved) {
+ try {
+ unregisterFromServer();
+ } catch (RemoteException e) {
+
+ }
+ }
+ }
+ }
+
+ protected abstract boolean registerWithServer() throws RemoteException;
+ protected abstract void unregisterFromServer() throws RemoteException;
+
+ protected interface ListenerOperation<TListener> {
+ void execute(TListener listener) throws RemoteException;
+ }
+
+ protected void foreach(ListenerOperation operation) {
+ Collection<TListener> listeners;
+ synchronized (mListeners) {
+ listeners = new ArrayList<TListener>(mListeners);
+ }
+
+ for (TListener listener : listeners) {
+ try {
+ operation.execute(listener);
+ } catch (RemoteException e) {
+ Log.e(mTag, "Error in monitored listener.", e);
+ // don't return, give a fair chance to all listeners to receive the event
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d6a8fb8..082a158 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -59,6 +59,7 @@
private final Context mContext;
private final ILocationManager mService;
private final GpsMeasurementListenerTransport mGpsMeasurementListenerTransport;
+ private final GpsNavigationMessageListenerTransport mGpsNavigationMessageListenerTransport;
private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners =
new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>();
private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners =
@@ -310,6 +311,8 @@
mService = service;
mContext = context;
mGpsMeasurementListenerTransport = new GpsMeasurementListenerTransport(mContext, mService);
+ mGpsNavigationMessageListenerTransport =
+ new GpsNavigationMessageListenerTransport(mContext, mService);
}
private LocationProvider createProvider(String name, ProviderProperties properties) {
@@ -1573,7 +1576,7 @@
/**
* Adds a GPS Measurement listener.
*
- * @param listener a {@link android.location.GpsMeasurementsEvent.Listener} object to register.
+ * @param listener a {@link GpsMeasurementsEvent.Listener} object to register.
* @return {@code true} if the listener was successfully registered, {@code false} otherwise.
*
* @hide
@@ -1593,6 +1596,30 @@
mGpsMeasurementListenerTransport.remove(listener);
}
+ /**
+ * Adds a GPS Navigation Message listener.
+ *
+ * @param listener a {@link GpsNavigationMessageEvent.Listener} object to register.
+ * @return {@code true} if the listener was successfully registered, {@code false} otherwise.
+ *
+ * @hide
+ */
+ public boolean addGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {
+ return mGpsNavigationMessageListenerTransport.add(listener);
+ }
+
+ /**
+ * Removes a GPS Navigation Message listener.
+ *
+ * @param listener a {@link GpsNavigationMessageEvent.Listener} object to remove.
+ *
+ * @hide
+ */
+ public void removeGpsNavigationMessageListener(
+ GpsNavigationMessageEvent.Listener listener) {
+ mGpsNavigationMessageListenerTransport.remove(listener);
+ }
+
/**
* Retrieves information about the current status of the GPS engine.
* This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
diff --git a/media/java/android/media/AudioAttributes.aidl b/media/java/android/media/AudioAttributes.aidl
new file mode 100644
index 0000000..04587f9
--- /dev/null
+++ b/media/java/android/media/AudioAttributes.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media;
+
+parcelable AudioAttributes;
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index ed1d441..6446273 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -93,11 +93,6 @@
*/
public final static int USAGE_NOTIFICATION_RINGTONE = 6;
/**
- * @hide
- * TEMPORARY TO PREVENT BUILD BREAKAGES DURING RENAME - DO NOT SHIP
- */
- public final static int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = USAGE_NOTIFICATION_RINGTONE;
- /**
* Usage value to use when the usage is a request to enter/end a
* communication, such as a VoIP communication or video-conference.
*/
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index a1a7af9..050c268 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -44,6 +44,7 @@
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiTvClient;
import android.hardware.usb.UsbManager;
import android.media.MediaPlayer.OnCompletionListener;
@@ -144,7 +145,23 @@
private final Context mContext;
private final ContentResolver mContentResolver;
private final AppOpsManager mAppOps;
- private final boolean mVoiceCapable;
+
+ // the platform has no specific capabilities
+ private static final int PLATFORM_DEFAULT = 0;
+ // the platform is voice call capable (a phone)
+ private static final int PLATFORM_VOICE = 1;
+ // the platform is a television or a set-top box
+ private static final int PLATFORM_TELEVISION = 2;
+ // the platform type affects volume and silent mode behavior
+ private final int mPlatformType;
+
+ private boolean isPlatformVoice() {
+ return mPlatformType == PLATFORM_VOICE;
+ }
+
+ private boolean isPlatformTelevision() {
+ return mPlatformType == PLATFORM_TELEVISION;
+ }
/** The controller for the volume UI. */
private final VolumeController mVolumeController = new VolumeController();
@@ -243,9 +260,10 @@
* NOTE: do not create loops in aliases!
* Some streams alias to different streams according to device category (phone or tablet) or
* use case (in call vs off call...). See updateStreamVolumeAlias() for more details.
- * mStreamVolumeAlias contains the default aliases for a voice capable device (phone) and
- * STREAM_VOLUME_ALIAS_NON_VOICE for a non voice capable device (tablet).*/
- private final int[] STREAM_VOLUME_ALIAS = new int[] {
+ * mStreamVolumeAlias contains STREAM_VOLUME_ALIAS_VOICE aliases for a voice capable device
+ * (phone), STREAM_VOLUME_ALIAS_TELEVISION for a television or set-top box and
+ * STREAM_VOLUME_ALIAS_DEFAULT for other devices (e.g. tablets).*/
+ private final int[] STREAM_VOLUME_ALIAS_VOICE = new int[] {
AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
AudioSystem.STREAM_RING, // STREAM_SYSTEM
AudioSystem.STREAM_RING, // STREAM_RING
@@ -257,7 +275,19 @@
AudioSystem.STREAM_RING, // STREAM_DTMF
AudioSystem.STREAM_MUSIC // STREAM_TTS
};
- private final int[] STREAM_VOLUME_ALIAS_NON_VOICE = new int[] {
+ private final int[] STREAM_VOLUME_ALIAS_TELEVISION = new int[] {
+ AudioSystem.STREAM_MUSIC, // STREAM_VOICE_CALL
+ AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM
+ AudioSystem.STREAM_MUSIC, // STREAM_RING
+ AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
+ AudioSystem.STREAM_MUSIC, // STREAM_ALARM
+ AudioSystem.STREAM_MUSIC, // STREAM_NOTIFICATION
+ AudioSystem.STREAM_MUSIC, // STREAM_BLUETOOTH_SCO
+ AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM_ENFORCED
+ AudioSystem.STREAM_MUSIC, // STREAM_DTMF
+ AudioSystem.STREAM_MUSIC // STREAM_TTS
+ };
+ private final int[] STREAM_VOLUME_ALIAS_DEFAULT = new int[] {
AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM
AudioSystem.STREAM_RING, // STREAM_RING
@@ -455,9 +485,12 @@
public final static int STREAM_REMOTE_MUSIC = -200;
// Devices for which the volume is fixed and VolumePanel slider should be disabled
- final int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_HDMI |
+ int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_HDMI |
AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
- AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
+ AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET |
+ AudioSystem.DEVICE_OUT_HDMI_ARC |
+ AudioSystem.DEVICE_OUT_SPDIF |
+ AudioSystem.DEVICE_OUT_AUX_LINE;
// TODO merge orientation and rotation
private final boolean mMonitorOrientation;
@@ -491,8 +524,16 @@
mContext = context;
mContentResolver = context.getContentResolver();
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
- mVoiceCapable = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_voice_capable);
+
+ if (mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_voice_capable)) {
+ mPlatformType = PLATFORM_VOICE;
+ } else if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEVISION)) {
+ mPlatformType = PLATFORM_TELEVISION;
+ } else {
+ mPlatformType = PLATFORM_DEFAULT;
+ }
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent");
@@ -622,10 +663,15 @@
BluetoothProfile.A2DP);
}
- HdmiControlManager hdmiManager =
+ mHdmiManager =
(HdmiControlManager) mContext.getSystemService(Context.HDMI_CONTROL_SERVICE);
- // Null if device is not Tv.
- mHdmiTvClient = hdmiManager.getTvClient();
+ if (mHdmiManager != null) {
+ synchronized (mHdmiManager) {
+ mHdmiTvClient = mHdmiManager.getTvClient();
+ mHdmiPlaybackClient = mHdmiManager.getPlaybackClient();
+ mHdmiCecSink = false;
+ }
+ }
sendMsg(mAudioHandler,
MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED,
@@ -670,6 +716,14 @@
}
}
+ private void checkAllFixedVolumeDevices()
+ {
+ int numStreamTypes = AudioSystem.getNumStreamTypes();
+ for (int streamType = 0; streamType < numStreamTypes; streamType++) {
+ mStreamStates[streamType].checkFixedVolumeDevices();
+ }
+ }
+
private void createStreamStates() {
int numStreamTypes = AudioSystem.getNumStreamTypes();
VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
@@ -678,6 +732,7 @@
streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[mStreamVolumeAlias[i]], i);
}
+ checkAllFixedVolumeDevices();
checkAllAliasStreamVolumes();
}
@@ -702,19 +757,32 @@
private void updateStreamVolumeAlias(boolean updateVolumes) {
int dtmfStreamAlias;
- if (mVoiceCapable) {
- mStreamVolumeAlias = STREAM_VOLUME_ALIAS;
+
+ switch (mPlatformType) {
+ case PLATFORM_VOICE:
+ mStreamVolumeAlias = STREAM_VOLUME_ALIAS_VOICE;
dtmfStreamAlias = AudioSystem.STREAM_RING;
- } else {
- mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NON_VOICE;
+ break;
+ case PLATFORM_TELEVISION:
+ mStreamVolumeAlias = STREAM_VOLUME_ALIAS_TELEVISION;
+ dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
+ break;
+ default:
+ mStreamVolumeAlias = STREAM_VOLUME_ALIAS_DEFAULT;
dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
}
- if (isInCommunication()) {
- dtmfStreamAlias = AudioSystem.STREAM_VOICE_CALL;
- mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_DTMF);
+
+ if (isPlatformTelevision()) {
+ mRingerModeAffectedStreams = 0;
} else {
- mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_DTMF);
+ if (isInCommunication()) {
+ dtmfStreamAlias = AudioSystem.STREAM_VOICE_CALL;
+ mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_DTMF);
+ } else {
+ mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_DTMF);
+ }
}
+
mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
if (updateVolumes) {
mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias]);
@@ -768,7 +836,7 @@
if (ringerMode != ringerModeFromSettings) {
Settings.Global.putInt(cr, Settings.Global.MODE_RINGER, ringerMode);
}
- if (mUseFixedVolume) {
+ if (mUseFixedVolume || isPlatformTelevision()) {
ringerMode = AudioManager.RINGER_MODE_NORMAL;
}
synchronized(mSettingsLock) {
@@ -815,9 +883,6 @@
broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
- // Restore the default media button receiver from the system settings
- mMediaFocusControl.restoreMediaButtonReceiver();
-
// Load settings for the volume controller
mVolumeController.loadSettings(cr);
}
@@ -1001,15 +1066,30 @@
// Check if volume update should be send to Hdmi system audio.
int newIndex = mStreamStates[streamType].getIndex(device);
- if (mHdmiTvClient != null &&
- streamTypeAlias == AudioSystem.STREAM_MUSIC &&
- (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) == 0 &&
- oldIndex != newIndex) {
- int maxIndex = getStreamMaxVolume(streamType);
- synchronized (mHdmiTvClient) {
- if (mHdmiSystemAudioSupported) {
- mHdmiTvClient.setSystemAudioVolume(
- (oldIndex + 5) / 10, (newIndex + 5) / 10, maxIndex);
+ if (mHdmiManager != null) {
+ synchronized (mHdmiManager) {
+ if (mHdmiTvClient != null &&
+ streamTypeAlias == AudioSystem.STREAM_MUSIC &&
+ (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) == 0 &&
+ oldIndex != newIndex) {
+ int maxIndex = getStreamMaxVolume(streamType);
+ synchronized (mHdmiTvClient) {
+ if (mHdmiSystemAudioSupported) {
+ mHdmiTvClient.setSystemAudioVolume(
+ (oldIndex + 5) / 10, (newIndex + 5) / 10, maxIndex);
+ }
+ }
+ }
+ // mHdmiCecSink true => mHdmiPlaybackClient != null
+ if (mHdmiCecSink &&
+ streamTypeAlias == AudioSystem.STREAM_MUSIC &&
+ oldIndex != newIndex) {
+ synchronized (mHdmiPlaybackClient) {
+ int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
+ KeyEvent.KEYCODE_VOLUME_UP;
+ mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
+ mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
+ }
}
}
}
@@ -1113,15 +1193,19 @@
}
}
- if (mHdmiTvClient != null &&
- streamTypeAlias == AudioSystem.STREAM_MUSIC &&
- (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) == 0 &&
- oldIndex != index) {
- int maxIndex = getStreamMaxVolume(streamType);
- synchronized (mHdmiTvClient) {
- if (mHdmiSystemAudioSupported) {
- mHdmiTvClient.setSystemAudioVolume(
- (oldIndex + 5) / 10, (index + 5) / 10, maxIndex);
+ if (mHdmiManager != null) {
+ synchronized (mHdmiManager) {
+ if (mHdmiTvClient != null &&
+ streamTypeAlias == AudioSystem.STREAM_MUSIC &&
+ (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) == 0 &&
+ oldIndex != index) {
+ int maxIndex = getStreamMaxVolume(streamType);
+ synchronized (mHdmiTvClient) {
+ if (mHdmiSystemAudioSupported) {
+ mHdmiTvClient.setSystemAudioVolume(
+ (oldIndex + 5) / 10, (index + 5) / 10, maxIndex);
+ }
+ }
}
}
}
@@ -1260,7 +1344,7 @@
// UI update and Broadcast Intent
private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
- if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
+ if (!isPlatformVoice() && (streamType == AudioSystem.STREAM_RING)) {
streamType = AudioSystem.STREAM_NOTIFICATION;
}
@@ -1349,10 +1433,14 @@
}
if (isStreamAffectedByMute(streamType)) {
- if (streamType == AudioSystem.STREAM_MUSIC && mHdmiTvClient != null) {
- synchronized (mHdmiTvClient) {
- if (mHdmiSystemAudioSupported) {
- mHdmiTvClient.setSystemAudioMute(state);
+ if (mHdmiManager != null) {
+ synchronized (mHdmiManager) {
+ if (streamType == AudioSystem.STREAM_MUSIC && mHdmiTvClient != null) {
+ synchronized (mHdmiTvClient) {
+ if (mHdmiSystemAudioSupported) {
+ mHdmiTvClient.setSystemAudioMute(state);
+ }
+ }
}
}
}
@@ -1475,11 +1563,15 @@
/** @see AudioManager#getMasterStreamType() */
public int getMasterStreamType() {
- if (mVoiceCapable) {
- return AudioSystem.STREAM_RING;
- } else {
- return AudioSystem.STREAM_NOTIFICATION;
+ switch (mPlatformType) {
+ case PLATFORM_VOICE:
+ return AudioSystem.STREAM_RING;
+ case PLATFORM_TELEVISION:
+ return AudioSystem.STREAM_MUSIC;
+ default:
+ break;
}
+ return AudioSystem.STREAM_NOTIFICATION;
}
/** @see AudioManager#setMicrophoneMute(boolean) */
@@ -1507,7 +1599,7 @@
/** @see AudioManager#setRingerMode(int) */
public void setRingerMode(int ringerMode) {
- if (mUseFixedVolume) {
+ if (mUseFixedVolume || isPlatformTelevision()) {
return;
}
@@ -1537,7 +1629,7 @@
ringerMode == AudioManager.RINGER_MODE_NORMAL) {
// ring and notifications volume should never be 0 when not silenced
// on voice capable devices
- if (mVoiceCapable &&
+ if (isPlatformVoice() &&
mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) {
synchronized (mStreamStates[streamType]) {
Set set = mStreamStates[streamType].mIndex.entrySet();
@@ -2044,6 +2136,7 @@
// muted by ringer mode have the correct volume
setRingerModeInt(getRingerMode(), false);
+ checkAllFixedVolumeDevices();
checkAllAliasStreamVolumes();
synchronized (mSafeMediaVolumeState) {
@@ -2763,11 +2856,18 @@
(1 << AudioSystem.STREAM_NOTIFICATION)|
(1 << AudioSystem.STREAM_SYSTEM);
- if (mVoiceCapable) {
- ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
- } else {
- ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
+ switch (mPlatformType) {
+ case PLATFORM_VOICE:
+ ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
+ break;
+ case PLATFORM_TELEVISION:
+ ringerModeAffectedStreams = 0;
+ break;
+ default:
+ ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
+ break;
}
+
synchronized (mCameraSoundForced) {
if (mCameraSoundForced) {
ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
@@ -2836,7 +2936,8 @@
}
private int getActiveStreamType(int suggestedStreamType) {
- if (mVoiceCapable) {
+ switch (mPlatformType) {
+ case PLATFORM_VOICE:
if (isInCommunication()) {
if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
== AudioSystem.FORCE_BT_SCO) {
@@ -2866,12 +2967,26 @@
if (DEBUG_VOL)
Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
return AudioSystem.STREAM_MUSIC;
- } else {
- if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
- + suggestedStreamType);
- return suggestedStreamType;
}
- } else {
+ break;
+ case PLATFORM_TELEVISION:
+ if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
+ if (isAfMusicActiveRecently(DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS)) {
+ if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: forcing STREAM_MUSIC");
+ return AudioSystem.STREAM_MUSIC;
+ } else if (mMediaFocusControl.checkUpdateRemoteStateIfActive(
+ AudioSystem.STREAM_MUSIC)) {
+ if (DEBUG_VOL)
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
+ return STREAM_REMOTE_MUSIC;
+ } else {
+ if (DEBUG_VOL) Log.v(TAG,
+ "getActiveStreamType: using STREAM_MUSIC as default");
+ return AudioSystem.STREAM_MUSIC;
+ }
+ }
+ break;
+ default:
if (isInCommunication()) {
if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
== AudioSystem.FORCE_BT_SCO) {
@@ -2902,12 +3017,12 @@
"getActiveStreamType: using STREAM_NOTIFICATION as default");
return AudioSystem.STREAM_NOTIFICATION;
}
- } else {
- if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
- + suggestedStreamType);
- return suggestedStreamType;
}
+ break;
}
+ if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
+ + suggestedStreamType);
+ return suggestedStreamType;
}
private void broadcastRingerMode(int ringerMode) {
@@ -3101,13 +3216,7 @@
continue;
}
- // ignore settings for fixed volume devices: volume should always be at max or 0
- if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) &&
- ((device & mFixedVolumeDevices) != 0)) {
- mIndex.put(device, (index != 0) ? mIndexMax : 0);
- } else {
- mIndex.put(device, getValidIndex(10 * index));
- }
+ mIndex.put(device, getValidIndex(10 * index));
}
}
}
@@ -3266,6 +3375,25 @@
return mStreamType;
}
+ public void checkFixedVolumeDevices() {
+ synchronized (VolumeStreamState.class) {
+ // ignore settings for fixed volume devices: volume should always be at max or 0
+ if (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) {
+ Set set = mIndex.entrySet();
+ Iterator i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ int device = ((Integer)entry.getKey()).intValue();
+ int index = ((Integer)entry.getValue()).intValue();
+ if (((device & mFixedVolumeDevices) != 0) && index != 0) {
+ entry.setValue(mIndexMax);
+ }
+ applyDeviceVolume(device);
+ }
+ }
+ }
+ }
+
private int getValidIndex(int index) {
if (index < 0) {
return 0;
@@ -3472,6 +3600,9 @@
if (mUseFixedVolume) {
return;
}
+ if (isPlatformTelevision() && (streamState.mStreamType != AudioSystem.STREAM_MUSIC)) {
+ return;
+ }
System.putIntForUser(mContentResolver,
streamState.getSettingNameForDevice(device),
(streamState.getIndex(device) + 5)/ 10,
@@ -3500,7 +3631,13 @@
loadTouchSoundAssets();
- mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
+ mSoundPool = new SoundPool.Builder()
+ .setMaxStreams(NUM_SOUNDPOOL_CHANNELS)
+ .setAudioAttributes(new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .build())
+ .build();
mSoundPoolCallBack = null;
mSoundPoolListenerThread = new SoundPoolListenerThread();
mSoundPoolListenerThread.start();
@@ -3750,7 +3887,8 @@
break;
case MSG_MEDIA_SERVER_DIED:
- if (AudioSystem.checkAudioFlinger() != AudioSystem.AUDIO_STATUS_OK) {
+ if (!mSystemReady ||
+ (AudioSystem.checkAudioFlinger() != AudioSystem.AUDIO_STATUS_OK)) {
Log.e(TAG, "Media server died.");
sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
null, 500);
@@ -3821,11 +3959,13 @@
mDockAudioMediaEnabled ?
AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE);
}
-
- if (mHdmiTvClient != null) {
- setHdmiSystemAudioSupported(mHdmiSystemAudioSupported);
+ if (mHdmiManager != null) {
+ synchronized (mHdmiManager) {
+ if (mHdmiTvClient != null) {
+ setHdmiSystemAudioSupported(mHdmiSystemAudioSupported);
+ }
+ }
}
-
// indicate the end of reconfiguration phase to audio HAL
AudioSystem.setParameters("restarting=false");
break;
@@ -4271,6 +4411,27 @@
null,
MUSIC_ACTIVE_POLL_PERIOD_MS);
}
+ // Television devices without CEC service apply software volume on HDMI output
+ if (isPlatformTelevision() && ((device & AudioSystem.DEVICE_OUT_HDMI) != 0)) {
+ mFixedVolumeDevices |= AudioSystem.DEVICE_OUT_HDMI;
+ checkAllFixedVolumeDevices();
+ if (mHdmiManager != null) {
+ synchronized (mHdmiManager) {
+ if (mHdmiPlaybackClient != null) {
+ mHdmiCecSink = false;
+ mHdmiPlaybackClient.queryDisplayStatus(mHdmiDisplayStatusCallback);
+ }
+ }
+ }
+ }
+ } else {
+ if (isPlatformTelevision() && ((device & AudioSystem.DEVICE_OUT_HDMI) != 0)) {
+ if (mHdmiManager != null) {
+ synchronized (mHdmiManager) {
+ mHdmiCecSink = false;
+ }
+ }
+ }
}
if (!isUsb && (device != AudioSystem.DEVICE_IN_WIRED_HEADSET)) {
sendDeviceConnectionIntent(device, state, name);
@@ -4573,18 +4734,20 @@
if (cameraSoundForced != mCameraSoundForced) {
mCameraSoundForced = cameraSoundForced;
- VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED];
- if (cameraSoundForced) {
- s.setAllIndexesToMax();
- mRingerModeAffectedStreams &=
- ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
- } else {
- s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM]);
- mRingerModeAffectedStreams |=
- (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ if (!isPlatformTelevision()) {
+ VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED];
+ if (cameraSoundForced) {
+ s.setAllIndexesToMax();
+ mRingerModeAffectedStreams &=
+ ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ } else {
+ s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM]);
+ mRingerModeAffectedStreams |=
+ (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ }
+ // take new state into account for streams muted by ringer mode
+ setRingerModeInt(getRingerMode(), false);
}
- // take new state into account for streams muted by ringer mode
- setRingerModeInt(getRingerMode(), false);
sendMsg(mAudioHandler,
MSG_SET_FORCE_USE,
@@ -4800,27 +4963,57 @@
// to HdmiControlService so that audio recevier can handle volume change.
//==========================================================================================
+ private class MyDisplayStatusCallback implements HdmiPlaybackClient.DisplayStatusCallback {
+ public void onComplete(int status) {
+ if (mHdmiManager != null) {
+ synchronized (mHdmiManager) {
+ mHdmiCecSink = (status != HdmiControlManager.POWER_STATUS_UNKNOWN);
+ // Television devices without CEC service apply software volume on HDMI output
+ if (isPlatformTelevision() && !mHdmiCecSink) {
+ mFixedVolumeDevices &= ~AudioSystem.DEVICE_OUT_HDMI;
+ }
+ checkAllFixedVolumeDevices();
+ }
+ }
+ }
+ };
+
// If HDMI-CEC system audio is supported
private boolean mHdmiSystemAudioSupported = false;
// Set only when device is tv.
private HdmiTvClient mHdmiTvClient;
+ // true if the device has system feature PackageManager.FEATURE_TELEVISION.
+ // cached HdmiControlManager interface
+ private HdmiControlManager mHdmiManager;
+ // Set only when device is a set-top box.
+ private HdmiPlaybackClient mHdmiPlaybackClient;
+ // true if we are a set-top box, an HDMI sink is connected and it supports CEC.
+ private boolean mHdmiCecSink;
+
+ private MyDisplayStatusCallback mHdmiDisplayStatusCallback = new MyDisplayStatusCallback();
@Override
public int setHdmiSystemAudioSupported(boolean on) {
- if (mHdmiTvClient == null) {
- Log.w(TAG, "Only Hdmi-Cec enabled TV device supports system audio mode.");
- return AudioSystem.DEVICE_NONE;
- }
+ int device = AudioSystem.DEVICE_NONE;
+ if (mHdmiManager != null) {
+ synchronized (mHdmiManager) {
+ if (mHdmiTvClient == null) {
+ Log.w(TAG, "Only Hdmi-Cec enabled TV device supports system audio mode.");
+ return device;
+ }
- synchronized (mHdmiTvClient) {
- if (mHdmiSystemAudioSupported == on) {
- return AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC);
+ synchronized (mHdmiTvClient) {
+ if (mHdmiSystemAudioSupported != on) {
+ mHdmiSystemAudioSupported = on;
+ AudioSystem.setForceUse(AudioSystem.FOR_HDMI_SYSTEM_AUDIO,
+ on ? AudioSystem.FORCE_HDMI_SYSTEM_AUDIO_ENFORCED :
+ AudioSystem.FORCE_NONE);
+ }
+ device = AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC);
+ }
}
- mHdmiSystemAudioSupported = on;
- AudioSystem.setForceUse(AudioSystem.FOR_HDMI_SYSTEM_AUDIO,
- on ? AudioSystem.FORCE_HDMI_SYSTEM_AUDIO_ENFORCED : AudioSystem.FORCE_NONE);
}
- return AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC);
+ return device;
}
//==========================================================================================
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
index 6004ecf..c67e397 100644
--- a/media/java/android/media/MediaFocusControl.java
+++ b/media/java/android/media/MediaFocusControl.java
@@ -78,7 +78,6 @@
private final Context mContext;
private final ContentResolver mContentResolver;
private final AudioService.VolumeController mVolumeController;
- private final BroadcastReceiver mReceiver = new PackageIntentsReceiver();
private final AppOpsManager mAppOps;
private final KeyguardManager mKeyguardManager;
private final AudioService mAudioService;
@@ -103,16 +102,6 @@
mContext.getSystemService(Context.TELEPHONY_SERVICE);
tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
- // Register for package addition/removal/change intent broadcasts
- // for media button receiver persistence
- IntentFilter pkgFilter = new IntentFilter();
- pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
- pkgFilter.addDataScheme("package");
- mContext.registerReceiver(mReceiver, pkgFilter);
-
mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
mKeyguardManager =
(KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
@@ -321,7 +310,6 @@
//==========================================================================================
// event handler messages
- private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 0;
private static final int MSG_RCDISPLAY_CLEAR = 1;
private static final int MSG_RCDISPLAY_UPDATE = 2;
private static final int MSG_REEVALUATE_REMOTE = 3;
@@ -362,10 +350,6 @@
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
- case MSG_PERSIST_MEDIABUTTONRECEIVER:
- onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
- break;
-
case MSG_RCDISPLAY_CLEAR:
onRcDisplayClear();
break;
@@ -874,29 +858,6 @@
}
- private class PackageIntentsReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
- || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) {
- if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- // a package is being removed, not replaced
- String packageName = intent.getData().getSchemeSpecificPart();
- if (packageName != null) {
- cleanupMediaButtonReceiverForPackage(packageName, true);
- }
- }
- } else if (action.equals(Intent.ACTION_PACKAGE_ADDED)
- || action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
- String packageName = intent.getData().getSchemeSpecificPart();
- if (packageName != null) {
- cleanupMediaButtonReceiverForPackage(packageName, false);
- }
- }
- }
- }
-
private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
if (keyEvent == null) {
return false;
@@ -1113,85 +1074,6 @@
/**
* Helper function:
- * Remove any entry in the remote control stack that has the same package name as packageName
- * Pre-condition: packageName != null
- */
- private void cleanupMediaButtonReceiverForPackage(String packageName, boolean removeAll) {
- synchronized(mPRStack) {
- if (mPRStack.empty()) {
- return;
- } else {
- final PackageManager pm = mContext.getPackageManager();
- PlayerRecord oldTop = mPRStack.peek();
- Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- // iterate over the stack entries
- // (using an iterator on the stack so we can safely remove an entry after having
- // evaluated it, traversal order doesn't matter here)
- while(stackIterator.hasNext()) {
- PlayerRecord prse = stackIterator.next();
- if (removeAll
- && packageName.equals(prse.getMediaButtonIntent().getCreatorPackage()))
- {
- // a stack entry is from the package being removed, remove it from the stack
- stackIterator.remove();
- prse.destroy();
- } else if (prse.getMediaButtonReceiver() != null) {
- try {
- // Check to see if this receiver still exists.
- pm.getReceiverInfo(prse.getMediaButtonReceiver(), 0);
- } catch (PackageManager.NameNotFoundException e) {
- // Not found -- remove it!
- stackIterator.remove();
- prse.destroy();
- }
- }
- }
- if (mPRStack.empty()) {
- // no saved media button receiver
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
- null));
- } else if (oldTop != mPRStack.peek()) {
- // the top of the stack has changed, save it in the system settings
- // by posting a message to persist it; only do this however if it has
- // a concrete component name (is not a transient registration)
- PlayerRecord prse = mPRStack.peek();
- if (prse.getMediaButtonReceiver() != null) {
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
- prse.getMediaButtonReceiver()));
- }
- }
- }
- }
- }
-
- /**
- * Helper function:
- * Restore remote control receiver from the system settings.
- */
- protected void restoreMediaButtonReceiver() {
- String receiverName = Settings.System.getStringForUser(mContentResolver,
- Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
- if ((null != receiverName) && !receiverName.isEmpty()) {
- ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
- if (eventReceiver == null) {
- // an invalid name was persisted
- return;
- }
- // construct a PendingIntent targeted to the restored component name
- // for the media button and register it
- Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
- // the associated intent will be handled by the component being registered
- mediaButtonIntent.setComponent(eventReceiver);
- PendingIntent pi = PendingIntent.getBroadcast(mContext,
- 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
- registerMediaButtonIntent(pi, eventReceiver, null /*token*/);
- }
- }
-
- /**
- * Helper function:
* Push the new media button receiver "near" the top of the PlayerRecord stack.
* "Near the top" is defined as:
* - at the top if the current PlayerRecord at the top is not playing
@@ -1258,13 +1140,6 @@
}
}
- topChanged = (oldTopPrse != mPRStack.lastElement());
- // post message to persist the default media button receiver
- if (topChanged && (target != null)) {
- mEventHandler.sendMessage( mEventHandler.obtainMessage(
- MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
- }
-
} catch (ArrayIndexOutOfBoundsException e) {
// not expected to happen, indicates improper concurrent modification or bad index
Log.e(TAG, "Wrong index (inStack=" + inStackIndex + " lastPlaying=" + lastPlayingIndex
@@ -1309,13 +1184,6 @@
return false;
}
- private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
- Settings.System.putStringForUser(mContentResolver,
- Settings.System.MEDIA_BUTTON_RECEIVER,
- receiver == null ? "" : receiver.flattenToString(),
- UserHandle.USER_CURRENT);
- }
-
//==========================================================================================
// Remote control display / client
//==========================================================================================
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 16a2235..a95348f 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -65,6 +65,13 @@
* <tr><td>{@link #KEY_SAMPLE_RATE}</td><td>Integer</td><td></td></tr>
* <tr><td>{@link #KEY_IS_ADTS}</td><td>Integer</td><td>optional, if <em>decoding</em> AAC audio content, setting this key to 1 indicates that each audio frame is prefixed by the ADTS header.</td></tr>
* <tr><td>{@link #KEY_AAC_PROFILE}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is AAC audio, specifies the desired profile.</td></tr>
+ * <tr><td>{@link #KEY_AAC_SBR_MODE}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is AAC audio, specifies the desired SBR mode.</td></tr>
+ * <tr><td>{@link #KEY_AAC_DRC_TARGET_REFERENCE_LEVEL}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the target reference level.</td></tr>
+ * <tr><td>{@link #KEY_AAC_ENCODED_TARGET_LEVEL}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the target reference level used at encoder.</td></tr>
+ * <tr><td>{@link #KEY_AAC_DRC_BOOST_FACTOR}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the DRC boost factor.</td></tr>
+ * <tr><td>{@link #KEY_AAC_DRC_ATTENUATION_FACTOR}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the DRC attenuation factor.</td></tr>
+ * <tr><td>{@link #KEY_AAC_DRC_HEAVY_COMPRESSION}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies whether to use heavy compression.</td></tr>
+ * <tr><td>{@link #KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the maximum number of channels the decoder outputs.</td></tr>
* <tr><td>{@link #KEY_CHANNEL_MASK}</td><td>Integer</td><td>optional, a mask of audio channel assignments</td></tr>
* <tr><td>{@link #KEY_FLAC_COMPRESSION_LEVEL}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is FLAC audio, specifies the desired compression level.</td></tr>
* </table>
@@ -206,6 +213,18 @@
public static final String KEY_I_FRAME_INTERVAL = "i-frame-interval";
/**
+ * A key describing the temporal layering schema. This is an optional parameter
+ * that applies only to video encoders. Use {@link MediaCodec#getInputFormat}
+ * after {@link MediaCodec#configure configure} to query if the encoder supports
+ * the desired schema. Supported values are {@code webrtc.vp8.1-layer},
+ * {@code webrtc.vp8.2-layer}, {@code webrtc.vp8.3-layer}, and {@code none}.
+ * If the encoder does not support temporal layering, the input format will
+ * not have an entry with this key.
+ * The associated value is a string.
+ */
+ public static final String KEY_TEMPORAL_LAYERING = "ts-schema";
+
+ /**
* @hide
*/
public static final String KEY_STRIDE = "stride";
@@ -261,6 +280,110 @@
public static final String KEY_AAC_PROFILE = "aac-profile";
/**
+ * A key describing the AAC SBR mode to be used (AAC audio formats only).
+ * The associated value is an integer and can be set to following values:
+ * <ul>
+ * <li>0 - no SBR should be applied</li>
+ * <li>1 - single rate SBR</li>
+ * <li>2 - double rate SBR</li>
+ * </ul>
+ * Note: If this key is not defined the default SRB mode for the desired AAC profile will
+ * be used.
+ * <p>This key is only used during encoding.
+ */
+ public static final String KEY_AAC_SBR_MODE = "aac-sbr-mode";
+
+ /**
+ * A key describing the maximum number of channels that can be output by the AAC decoder.
+ * By default, the decoder will output the same number of channels as present in the encoded
+ * stream, if supported. Set this value to limit the number of output channels, and use
+ * the downmix information in the stream, if available.
+ * <p>Values larger than the number of channels in the content to decode are ignored.
+ * <p>This key is only used during decoding.
+ */
+ public static final String KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT = "aac-max-output-channel_count";
+
+ /**
+ * A key describing a gain to be applied so that the output loudness matches the
+ * Target Reference Level. This is typically used to normalize loudness across program items.
+ * The gain is derived as the difference between the Target Reference Level and the
+ * Program Reference Level. The latter can be given in the bitstream and indicates the actual
+ * loudness value of the program item.
+ * <p>The value is given as an integer value between
+ * 0 and 127, and is calculated as -0.25 * Target Reference Level in dBFS.
+ * Therefore, it represents the range of Full Scale (0 dBFS) to -31.75 dBFS.
+ * <p>This key is only used during decoding.
+ */
+ public static final String KEY_AAC_DRC_TARGET_REFERENCE_LEVEL = "aac-target-ref-level";
+
+ /**
+ * A key describing the target reference level that was assumed at the encoder for
+ * calculation of attenuation gains for clipping prevention. This information can be provided
+ * if it is known, otherwise a worst-case assumption is used.
+ * <p>The value is given as an integer value between
+ * 0 and 127, and is calculated as -0.25 * Target Reference Level in dBFS.
+ * Therefore, it represents the range of Full Scale (0 dBFS) to -31.75 dBFS.
+ * The default value is the worst-case assumption of 127.
+ * <p>The value is ignored when heavy compression is used (see
+ * {@link #KEY_AAC_DRC_HEAVY_COMPRESSION}).
+ * <p>This key is only used during decoding.
+ */
+ public static final String KEY_AAC_ENCODED_TARGET_LEVEL = "aac-encoded-target-level";
+
+ /**
+ * A key describing the boost factor allowing to adapt the dynamics of the output to the
+ * actual listening requirements. This relies on DRC gain sequences that can be transmitted in
+ * the encoded bitstream to be able to reduce the dynamics of the output signal upon request.
+ * This factor enables the user to select how much of the gains are applied.
+ * <p>Positive gains (boost) and negative gains (attenuation, see
+ * {@link #KEY_AAC_DRC_ATTENUATION_FACTOR}) can be controlled separately for a better match
+ * to different use-cases.
+ * <p>Typically, attenuation gains are sent for loud signal segments, and boost gains are sent
+ * for soft signal segments. If the output is listened to in a noisy environment, for example,
+ * the boost factor is used to enable the positive gains, i.e. to amplify soft signal segments
+ * beyond the noise floor. But for listening late at night, the attenuation
+ * factor is used to enable the negative gains, to prevent loud signal from surprising
+ * the listener. In applications which generally need a low dynamic range, both the boost factor
+ * and the attenuation factor are used in order to enable all DRC gains.
+ * <p>In order to prevent clipping, it is also recommended to apply the attenuation factors
+ * in case of a downmix and/or loudness normalization to high target reference levels.
+ * <p>Both the boost and the attenuation factor parameters are given as integer values
+ * between 0 and 127, representing the range of the factor of 0 (i.e. don't apply)
+ * to 1 (i.e. fully apply boost/attenuation factors respectively).
+ * <p>This key is only used during decoding.
+ */
+ public static final String KEY_AAC_DRC_BOOST_FACTOR = "aac-drc-boost-level";
+
+ /**
+ * A key describing the attenuation factor allowing to adapt the dynamics of the output to the
+ * actual listening requirements.
+ * See {@link #KEY_AAC_DRC_BOOST_FACTOR} for a description of the role of this attenuation
+ * factor and the value range.
+ * <p>This key is only used during decoding.
+ */
+ public static final String KEY_AAC_DRC_ATTENUATION_FACTOR = "aac-drc-cut-level";
+
+ /**
+ * A key describing the selection of the heavy compression profile for DRC.
+ * Two separate DRC gain sequences can be transmitted in one bitstream: MPEG-4 DRC light
+ * compression, and DVB-specific heavy compression. When selecting the application of the heavy
+ * compression, one of the sequences is selected:
+ * <ul>
+ * <li>0 enables light compression,</li>
+ * <li>1 enables heavy compression instead.
+ * </ul>
+ * Note that only light compression offers the features of scaling of DRC gains
+ * (see {@link #KEY_AAC_DRC_BOOST_FACTOR} and {@link #KEY_AAC_DRC_ATTENUATION_FACTOR} for the
+ * boost and attenuation factors, and frequency-selective (multiband) DRC.
+ * Light compression usually contains clipping prevention for stereo downmixing while heavy
+ * compression, if additionally provided in the bitstream, is usually stronger, and contains
+ * clipping prevention for stereo and mono downmixing.
+ * <p>The default is light compression.
+ * <p>This key is only used during decoding.
+ */
+ public static final String KEY_AAC_DRC_HEAVY_COMPRESSION = "aac-drc-heavy-compression";
+
+ /**
* A key describing the FLAC compression level to be used (FLAC audio format only).
* The associated value is an integer ranging from 0 (fastest, least compression)
* to 8 (slowest, most compression).
diff --git a/media/java/android/media/MediaMetadataEditor.java b/media/java/android/media/MediaMetadataEditor.java
index ca44e9d..566b93f 100644
--- a/media/java/android/media/MediaMetadataEditor.java
+++ b/media/java/android/media/MediaMetadataEditor.java
@@ -17,6 +17,7 @@
package android.media;
import android.graphics.Bitmap;
+import android.media.session.MediaSession;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
@@ -26,8 +27,10 @@
* An abstract class for editing and storing metadata that can be published by
* {@link RemoteControlClient}. See the {@link RemoteControlClient#editMetadata(boolean)}
* method to instantiate a {@link RemoteControlClient.MetadataEditor} object.
+ *
+ * @deprecated Use {@link MediaMetadata} instead together with {@link MediaSession}.
*/
-public abstract class MediaMetadataEditor {
+@Deprecated public abstract class MediaMetadataEditor {
private final static String TAG = "MediaMetadataEditor";
/**
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 7d1de24..7cda961 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -2220,7 +2220,9 @@
}
} else {
// We only know how to handle local and remote, fall back to local if not remote.
- session.setPlaybackToLocal(mPlaybackStream);
+ AudioAttributes.Builder bob = new AudioAttributes.Builder();
+ bob.setLegacyStreamType(mPlaybackStream);
+ session.setPlaybackToLocal(bob.build());
mSvp = null;
}
}
diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java
index eb543b4..54ad60e 100644
--- a/media/java/android/media/Metadata.java
+++ b/media/java/android/media/Metadata.java
@@ -40,8 +40,9 @@
the right get* method to fetch its value.
@hide
+ @deprecated Use {@link MediaMetadata}.
*/
-public class Metadata
+@Deprecated public class Metadata
{
// The metadata are keyed using integers rather than more heavy
// weight strings. We considered using Bundle to ship the metadata
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 740a9d3..db6315a 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -18,28 +18,20 @@
import android.app.PendingIntent;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.RectF;
import android.media.session.MediaSessionLegacyHelper;
import android.media.session.PlaybackState;
import android.media.session.MediaSession;
import android.os.Bundle;
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.SystemClock;
import android.util.Log;
import java.lang.IllegalArgumentException;
-import java.util.ArrayList;
-import java.util.Iterator;
/**
* RemoteControlClient enables exposing information meant to be consumed by remote controls
@@ -63,8 +55,10 @@
* // create and register the remote control client
* RemoteControlClient myRemoteControlClient = new RemoteControlClient(mediaPendingIntent);
* myAudioManager.registerRemoteControlClient(myRemoteControlClient);</pre>
+ *
+ * @deprecated Use {@link MediaSession} instead.
*/
-public class RemoteControlClient
+@Deprecated public class RemoteControlClient
{
private final static String TAG = "RemoteControlClient";
private final static boolean DEBUG = false;
@@ -425,8 +419,10 @@
* has been set, use {@link #apply()} to make it the new metadata that should be displayed
* for the associated client. Once the metadata has been "applied", you cannot reuse this
* instance of the MetadataEditor.
+ *
+ * @deprecated Use {@link MediaMetadata} and {@link MediaSession} instead.
*/
- public class MetadataEditor extends MediaMetadataEditor {
+ @Deprecated public class MetadataEditor extends MediaMetadataEditor {
// only use RemoteControlClient.editMetadata() to get a MetadataEditor instance
private MetadataEditor() { }
@@ -750,7 +746,8 @@
// USE_SESSIONS
if (mSession != null) {
PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState);
- bob.setActions(PlaybackState.getActionsFromRccControlFlags(transportControlFlags));
+ bob.setActions(
+ PlaybackState.getActionsFromRccControlFlags(transportControlFlags));
mSessionPlaybackState = bob.build();
mSession.setPlaybackState(mSessionPlaybackState);
}
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 9ea3f26..75a8952 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -23,8 +23,6 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
-import android.media.IRemoteControlDisplay;
-import android.media.MediaMetadataEditor;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSessionLegacyHelper;
@@ -57,8 +55,10 @@
* <p>
* Registration requires the {@link OnClientUpdateListener} listener to be one of the enabled
* notification listeners (see {@link android.service.notification.NotificationListenerService}).
+ *
+ * @deprecated Use {@link MediaController} instead.
*/
-public final class RemoteController
+@Deprecated public final class RemoteController
{
private final static int MAX_BITMAP_DIMENSION = 512;
private final static int TRANSPORT_UNKNOWN = 0;
@@ -395,12 +395,6 @@
mArtworkWidth = -1;
mArtworkHeight = -1;
}
- if (mIsRegistered) {
- mAudioManager.remoteControlDisplayUsesBitmapSize(mRcd,
- mArtworkWidth, mArtworkHeight);
- } // else new values have been stored, and will be read by AudioManager with
- // RemoteController.getArtworkSize() when AudioManager.registerRemoteController()
- // is called.
}
return true;
}
@@ -1023,8 +1017,8 @@
state.getPosition(), state.getPlaybackSpeed());
}
if (state != null) {
- l.onClientTransportControlUpdate(PlaybackState.getRccControlFlagsFromActions(state
- .getActions()));
+ l.onClientTransportControlUpdate(
+ PlaybackState.getRccControlFlagsFromActions(state.getActions()));
}
}
}
@@ -1042,7 +1036,8 @@
boolean canRate = mCurrentSession != null
&& mCurrentSession.getRatingType() != Rating.RATING_NONE;
long editableKeys = canRate ? MediaMetadataEditor.RATING_KEY_BY_USER : 0;
- Bundle legacyMetadata = MediaSessionLegacyHelper.getOldMetadata(metadata);
+ Bundle legacyMetadata = MediaSessionLegacyHelper.getOldMetadata(metadata,
+ mArtworkWidth, mArtworkHeight);
mMetadataEditor = new MetadataEditor(legacyMetadata, editableKeys);
metadataEditor = mMetadataEditor;
}
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 44c193f..9b9c767 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -126,12 +126,71 @@
* @param srcQuality the sample-rate converter quality. Currently has no
* effect. Use 0 for the default.
* @return a SoundPool object, or null if creation failed
+ * @deprecated use {@link SoundPool.Builder} instead to create and configure a
+ * SoundPool instance
*/
public SoundPool(int maxStreams, int streamType, int srcQuality) {
+ this(maxStreams,
+ new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build());
+ }
+
+ private SoundPool(int maxStreams, AudioAttributes attributes) {
if (SystemProperties.getBoolean("config.disable_media", false)) {
mImpl = new SoundPoolStub();
} else {
- mImpl = new SoundPoolImpl(this, maxStreams, streamType, srcQuality);
+ mImpl = new SoundPoolImpl(this, maxStreams, attributes);
+ }
+ }
+
+ /**
+ * Builder class for {@link SoundPool} objects.
+ */
+ public static class Builder {
+ private int mMaxStreams = 1;
+ private AudioAttributes mAudioAttributes;
+
+ /**
+ * Constructs a new Builder with the defaults format values.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Sets the maximum of number of simultaneous streams that can be played simultaneously.
+ * @param maxStreams a value equal to 1 or greater.
+ * @return the same Builder instance
+ * @throws IllegalArgumentException
+ */
+ public Builder setMaxStreams(int maxStreams) throws IllegalArgumentException {
+ if (maxStreams <= 0) {
+ throw new IllegalArgumentException(
+ "Strictly positive value required for the maximum number of streams");
+ }
+ mMaxStreams = maxStreams;
+ return this;
+ }
+
+ /**
+ * Sets the {@link AudioAttributes}. For examples, game applications will use attributes
+ * built with usage information set to {@link AudioAttributes#USAGE_GAME}.
+ * @param attributes a non-null
+ * @return
+ */
+ public Builder setAudioAttributes(AudioAttributes attributes)
+ throws IllegalArgumentException {
+ if (attributes == null) {
+ throw new IllegalArgumentException("Invalid null AudioAttributes");
+ }
+ mAudioAttributes = attributes;
+ return this;
+ }
+
+ public SoundPool build() {
+ if (mAudioAttributes == null) {
+ mAudioAttributes = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA).build();
+ }
+ return new SoundPool(mMaxStreams, mAudioAttributes);
}
}
@@ -457,7 +516,7 @@
private SoundPool mProxy;
private final Object mLock;
- private final int mStreamType;
+ private final AudioAttributes mAttributes;
private final IAppOpsService mAppOps;
// SoundPool messages
@@ -465,15 +524,15 @@
// must match SoundPool.h
private static final int SAMPLE_LOADED = 1;
- public SoundPoolImpl(SoundPool proxy, int maxStreams, int streamType, int srcQuality) {
+ public SoundPoolImpl(SoundPool proxy, int maxStreams, AudioAttributes attr) {
// do native setup
- if (native_setup(new WeakReference(this), maxStreams, streamType, srcQuality) != 0) {
+ if (native_setup(new WeakReference(this), maxStreams, attr) != 0) {
throw new RuntimeException("Native setup failed");
}
mLock = new Object();
mProxy = proxy;
- mStreamType = streamType;
+ mAttributes = attr;
IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
mAppOps = IAppOpsService.Stub.asInterface(b);
}
@@ -548,9 +607,9 @@
private boolean isRestricted() {
try {
- final int usage = AudioAttributes.usageForLegacyStreamType(mStreamType);
final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
- usage, Process.myUid(), ActivityThread.currentPackageName());
+ mAttributes.getUsage(),
+ Process.myUid(), ActivityThread.currentPackageName());
return mode != AppOpsManager.MODE_ALLOWED;
} catch (RemoteException e) {
return false;
@@ -648,7 +707,8 @@
public native final void release();
- private native final int native_setup(Object weakRef, int maxStreams, int streamType, int srcQuality);
+ private native final int native_setup(Object weakRef, int maxStreams,
+ Object/*AudioAttributes*/ attributes);
protected void finalize() { release(); }
}
diff --git a/media/java/android/media/browse/IMediaBrowserService.aidl b/media/java/android/media/browse/IMediaBrowserService.aidl
new file mode 100644
index 0000000..5126f6bd
--- /dev/null
+++ b/media/java/android/media/browse/IMediaBrowserService.aidl
@@ -0,0 +1,23 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+
+package android.media.browse;
+
+import android.content.res.Configuration;
+import android.media.browse.IMediaBrowserServiceCallbacks;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * Media API allows clients to browse through hierarchy of a user’s media collection,
+ * playback a specific media entry and interact with the now playing queue.
+ * @hide
+ */
+oneway interface IMediaBrowserService {
+ void connect(String pkg, in Bundle rootHints, IMediaBrowserServiceCallbacks callbacks);
+ void disconnect(IMediaBrowserServiceCallbacks callbacks);
+
+ void addSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
+ void removeSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
+ void loadThumbnail(in int seqNum, in Uri uri, int width, int height,
+ IMediaBrowserServiceCallbacks callbacks);
+}
\ No newline at end of file
diff --git a/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl b/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl
new file mode 100644
index 0000000..f6d5dae
--- /dev/null
+++ b/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl
@@ -0,0 +1,28 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+
+package android.media.browse;
+
+import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
+import android.media.session.MediaSession;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * Media API allows clients to browse through hierarchy of a user’s media collection,
+ * playback a specific media entry and interact with the now playing queue.
+ * @hide
+ */
+oneway interface IMediaBrowserServiceCallbacks {
+ /**
+ * Invoked when the connected has been established.
+ * @param root The root Uri for browsing.
+ * @param session The {@link MediaSession.Token media session token} that can be used to control
+ * the playback of the media app.
+ * @param extra Extras returned by the media service.
+ */
+ void onConnect(in Uri root, in MediaSession.Token session, in Bundle extras);
+ void onConnectFailed();
+ void onLoadChildren(in Uri uri, in ParceledListSlice list);
+ void onLoadThumbnail(int seqNum, in Bitmap bitmap);
+}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
new file mode 100644
index 0000000..f6ea360
--- /dev/null
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -0,0 +1,862 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.browse;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ParceledListSlice;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.media.session.MediaSession;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Browses media content offered by a link MediaBrowserService.
+ * <p>
+ * This object is not thread-safe. All calls should happen on the thread on which the browser
+ * was constructed.
+ * </p>
+ */
+public final class MediaBrowser {
+ private static final String TAG = "MediaBrowser";
+ private static final boolean DBG = false;
+
+ private static final int CONNECT_STATE_DISCONNECTED = 0;
+ private static final int CONNECT_STATE_CONNECTING = 1;
+ private static final int CONNECT_STATE_CONNECTED = 2;
+ private static final int CONNECT_STATE_SUSPENDED = 3;
+
+ private final Context mContext;
+ private final ComponentName mServiceComponent;
+ private final ConnectionCallback mCallback;
+ private final Bundle mRootHints;
+ private final Handler mHandler = new Handler();
+ private final ArrayMap<Uri,Subscription> mSubscriptions =
+ new ArrayMap<Uri, MediaBrowser.Subscription>();
+ private final SparseArray<ThumbnailRequest> mThumbnailRequests =
+ new SparseArray<ThumbnailRequest>();
+
+ private int mState = CONNECT_STATE_DISCONNECTED;
+ private MediaServiceConnection mServiceConnection;
+ private IMediaBrowserService mServiceBinder;
+ private IMediaBrowserServiceCallbacks mServiceCallbacks;
+ private Uri mRootUri;
+ private MediaSession.Token mMediaSessionToken;
+ private Bundle mExtras;
+ private int mNextSeq;
+
+ /**
+ * Creates a media browser for the specified media browse service.
+ *
+ * @param context The context.
+ * @param serviceComponent The component name of the media browse service.
+ * @param callback The connection callback.
+ * @param rootHints An optional bundle of service-specific arguments to send
+ * to the media browse service when connecting and retrieving the root uri
+ * for browsing, or null if none. The contents of this bundle may affect
+ * the information returned when browsing.
+ */
+ public MediaBrowser(Context context, ComponentName serviceComponent,
+ ConnectionCallback callback, Bundle rootHints) {
+ if (context == null) {
+ throw new IllegalArgumentException("context must not be null");
+ }
+ if (serviceComponent == null) {
+ throw new IllegalArgumentException("service component must not be null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("connection callback must not be null");
+ }
+ mContext = context;
+ mServiceComponent = serviceComponent;
+ mCallback = callback;
+ mRootHints = rootHints;
+ }
+
+ /**
+ * Connects to the media browse service.
+ * <p>
+ * The connection callback specified in the constructor will be invoked
+ * when the connection completes or fails.
+ * </p>
+ */
+ public void connect() {
+ if (mState != CONNECT_STATE_DISCONNECTED) {
+ throw new IllegalStateException("connect() called while not disconnected (state="
+ + getStateLabel(mState) + ")");
+ }
+ // TODO: remove this extra check.
+ if (DBG) {
+ if (mServiceConnection != null) {
+ throw new RuntimeException("mServiceConnection should be null. Instead it is "
+ + mServiceConnection);
+ }
+ }
+ if (mServiceBinder != null) {
+ throw new RuntimeException("mServiceBinder should be null. Instead it is "
+ + mServiceBinder);
+ }
+ if (mServiceCallbacks != null) {
+ throw new RuntimeException("mServiceCallbacks should be null. Instead it is "
+ + mServiceCallbacks);
+ }
+
+ mState = CONNECT_STATE_CONNECTING;
+
+ final Intent intent = new Intent(MediaBrowserService.SERVICE_ACTION);
+ intent.setComponent(mServiceComponent);
+
+ final ServiceConnection thisConnection = mServiceConnection = new MediaServiceConnection();
+
+ try {
+ mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ } catch (Exception ex) {
+ Log.e(TAG, "Failed binding to service " + mServiceComponent);
+
+ // Tell them that it didn't work. We are already on the main thread,
+ // but we don't want to do callbacks inside of connect(). So post it,
+ // and then check that we are on the same ServiceConnection. We know
+ // we won't also get an onServiceConnected or onServiceDisconnected,
+ // so we won't be doing double callbacks.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // Ensure that nobody else came in or tried to connect again.
+ if (thisConnection == mServiceConnection) {
+ forceCloseConnection();
+ mCallback.onConnectionFailed();
+ }
+ }
+ });
+ }
+
+ if (DBG) {
+ Log.d(TAG, "connect...");
+ dump();
+ }
+ }
+
+ /**
+ * Disconnects from the media browse service.
+ * After this, no more callbacks will be received.
+ */
+ public void disconnect() {
+ // It's ok to call this any state, because allowing this lets apps not have
+ // to check isConnected() unnecessarily. They won't appreciate the extra
+ // assertions for this. We do everything we can here to go back to a sane state.
+ if (mServiceCallbacks != null) {
+ try {
+ mServiceBinder.disconnect(mServiceCallbacks);
+ } catch (RemoteException ex) {
+ // We are disconnecting anyway. Log, just for posterity but it's not
+ // a big problem.
+ Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
+ }
+ }
+ forceCloseConnection();
+
+ if (DBG) {
+ Log.d(TAG, "disconnect...");
+ dump();
+ }
+ }
+
+ /**
+ * Null out the variables and unbind from the service. This doesn't include
+ * calling disconnect on the service, because we only try to do that in the
+ * clean shutdown cases.
+ * <p>
+ * Everywhere that calls this EXCEPT for disconnect() should follow it with
+ * a call to mCallback.onConnectionFailed(). Disconnect doesn't do that callback
+ * for a clean shutdown, but everywhere else is a dirty shutdown and should
+ * notify the app.
+ */
+ private void forceCloseConnection() {
+ if (mServiceConnection != null) {
+ mContext.unbindService(mServiceConnection);
+ }
+ mState = CONNECT_STATE_DISCONNECTED;
+ mServiceConnection = null;
+ mServiceBinder = null;
+ mServiceCallbacks = null;
+ mRootUri = null;
+ mMediaSessionToken = null;
+ }
+
+ /**
+ * Returns whether the browser is connected to the service.
+ */
+ public boolean isConnected() {
+ return mState == CONNECT_STATE_CONNECTED;
+ }
+
+ /**
+ * Gets the root Uri.
+ * <p>
+ * Note that the root uri may become invalid or change when when the
+ * browser is disconnected.
+ * </p>
+ *
+ * @throws IllegalStateException if not connected.
+ */
+ public @NonNull Uri getRoot() {
+ if (mState != CONNECT_STATE_CONNECTED) {
+ throw new IllegalStateException("getSessionToken() called while not connected (state="
+ + getStateLabel(mState) + ")");
+ }
+ return mRootUri;
+ }
+
+ /**
+ * Gets any extras for the media service.
+ *
+ * @throws IllegalStateException if not connected.
+ */
+ public @Nullable Bundle getExtras() {
+ if (mState != CONNECT_STATE_CONNECTED) {
+ throw new IllegalStateException("getExtras() called while not connected (state="
+ + getStateLabel(mState) + ")");
+ }
+ return mExtras;
+ }
+
+ /**
+ * Gets the media session token associated with the media browser.
+ * <p>
+ * Note that the session token may become invalid or change when when the
+ * browser is disconnected.
+ * </p>
+ *
+ * @return The session token for the browser, never null.
+ *
+ * @throws IllegalStateException if not connected.
+ */
+ public @NonNull MediaSession.Token getSessionToken() {
+ if (mState != CONNECT_STATE_CONNECTED) {
+ throw new IllegalStateException("getSessionToken() called while not connected (state="
+ + mState + ")");
+ }
+ return mMediaSessionToken;
+ }
+
+ /**
+ * Queries for information about the media items that are contained within
+ * the specified Uri and subscribes to receive updates when they change.
+ * <p>
+ * The list of subscriptions is maintained even when not connected and is
+ * restored after reconnection. It is ok to subscribe while not connected
+ * but the results will not be returned until the connection completes.
+ * </p><p>
+ * If the uri is already subscribed with a different callback then the new
+ * callback will replace the previous one.
+ * </p>
+ *
+ * @param parentUri The uri of the parent media item whose list of children
+ * will be subscribed.
+ * @param callback The callback to receive the list of children.
+ */
+ public void subscribe(@NonNull Uri parentUri, @NonNull SubscriptionCallback callback) {
+ // Check arguments.
+ if (parentUri == null) {
+ throw new IllegalArgumentException("parentUri is null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("callback is null");
+ }
+
+ // Update or create the subscription.
+ Subscription sub = mSubscriptions.get(parentUri);
+ boolean newSubscription = sub == null;
+ if (newSubscription) {
+ sub = new Subscription(parentUri);
+ mSubscriptions.put(parentUri, sub);
+ }
+ sub.callback = callback;
+
+ // If we are connected, tell the service that we are watching. If we aren't
+ // connected, the service will be told when we connect.
+ if (mState == CONNECT_STATE_CONNECTED && newSubscription) {
+ try {
+ mServiceBinder.addSubscription(parentUri, mServiceCallbacks);
+ } catch (RemoteException ex) {
+ // Process is crashing. We will disconnect, and upon reconnect we will
+ // automatically reregister. So nothing to do here.
+ Log.d(TAG, "addSubscription failed with RemoteException parentUri=" + parentUri);
+ }
+ }
+ }
+
+ /**
+ * Unsubscribes for changes to the children of the specified Uri.
+ * <p>
+ * The query callback will no longer be invoked for results associated with
+ * this Uri once this method returns.
+ * </p>
+ *
+ * @param parentUri The uri of the parent media item whose list of children
+ * will be unsubscribed.
+ */
+ public void unsubscribe(@NonNull Uri parentUri) {
+ // Check arguments.
+ if (parentUri == null) {
+ throw new IllegalArgumentException("parentUri is null");
+ }
+
+ // Remove from our list.
+ final Subscription sub = mSubscriptions.remove(parentUri);
+
+ // Tell the service if necessary.
+ if (mState == CONNECT_STATE_CONNECTED && sub != null) {
+ try {
+ mServiceBinder.removeSubscription(parentUri, mServiceCallbacks);
+ } catch (RemoteException ex) {
+ // Process is crashing. We will disconnect, and upon reconnect we will
+ // automatically reregister. So nothing to do here.
+ Log.d(TAG, "removeSubscription failed with RemoteException parentUri=" + parentUri);
+ }
+ }
+ }
+
+ /**
+ * Loads the thumbnail of a media item.
+ *
+ * @param uri The uri of the thumbnail.
+ * @param width The preferred width of the icon in dp.
+ * @param height The preferred width of the icon in dp.
+ * @param callback The callback to receive the thumbnail.
+ */
+ public void loadThumbnail(final @NonNull Uri uri, final int width, final int height,
+ final @NonNull ThumbnailCallback callback) {
+ if (uri == null) {
+ throw new IllegalArgumentException("thumbnail uri cannot be null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("thumbnail callback cannot be null");
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < mThumbnailRequests.size(); i++) {
+ ThumbnailRequest existingRequest = mThumbnailRequests.valueAt(i);
+ if (existingRequest.isSameRequest(uri, width, height)) {
+ existingRequest.addCallback(callback);
+ return;
+ }
+ }
+ final int seq = mNextSeq++;
+ ThumbnailRequest request = new ThumbnailRequest(seq, uri, width, height);
+ request.addCallback(callback);
+ mThumbnailRequests.put(seq, request);
+ if (mState == CONNECT_STATE_CONNECTED) {
+ try {
+ mServiceBinder.loadThumbnail(seq, uri, width, height, mServiceCallbacks);
+ } catch (RemoteException e) {
+ // Process is crashing. We will disconnect, and upon reconnect we will
+ // automatically reload the thumbnails. So nothing to do here.
+ Log.d(TAG, "loadThumbnail failed with RemoteException uri=" + uri);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * For debugging.
+ */
+ private static String getStateLabel(int state) {
+ switch (state) {
+ case CONNECT_STATE_DISCONNECTED:
+ return "CONNECT_STATE_DISCONNECTED";
+ case CONNECT_STATE_CONNECTING:
+ return "CONNECT_STATE_CONNECTING";
+ case CONNECT_STATE_CONNECTED:
+ return "CONNECT_STATE_CONNECTED";
+ case CONNECT_STATE_SUSPENDED:
+ return "CONNECT_STATE_SUSPENDED";
+ default:
+ return "UNKNOWN/" + state;
+ }
+ }
+
+ private final void onServiceConnected(final IMediaBrowserServiceCallbacks callback,
+ final Uri root, final MediaSession.Token session, final Bundle extra) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // Check to make sure there hasn't been a disconnect or a different
+ // ServiceConnection.
+ if (!isCurrent(callback, "onConnect")) {
+ return;
+ }
+ // Don't allow them to call us twice.
+ if (mState != CONNECT_STATE_CONNECTING) {
+ Log.w(TAG, "onConnect from service while mState="
+ + getStateLabel(mState) + "... ignoring");
+ return;
+ }
+ mRootUri = root;
+ mMediaSessionToken = session;
+ mExtras = extra;
+ mState = CONNECT_STATE_CONNECTED;
+
+ if (DBG) {
+ Log.d(TAG, "ServiceCallbacks.onConnect...");
+ dump();
+ }
+ mCallback.onConnected();
+
+ // we may receive some subscriptions before we are connected, so re-subscribe
+ // everything now
+ for (Uri uri : mSubscriptions.keySet()) {
+ try {
+ mServiceBinder.addSubscription(uri, mServiceCallbacks);
+ } catch (RemoteException ex) {
+ // Process is crashing. We will disconnect, and upon reconnect we will
+ // automatically reregister. So nothing to do here.
+ Log.d(TAG, "addSubscription failed with RemoteException parentUri=" + uri);
+ }
+ }
+
+ for (int i = 0; i < mThumbnailRequests.size(); i++) {
+ ThumbnailRequest request = mThumbnailRequests.valueAt(i);
+ try {
+ mServiceBinder.loadThumbnail(request.mSeq, request.mUri,
+ request.mWidth, request.mHeight, mServiceCallbacks);
+ } catch (RemoteException e) {
+ // Process is crashing. We will disconnect, and upon reconnect we will
+ // automatically reload. So nothing to do here.
+ Log.d(TAG, "loadThumbnail failed with RemoteException request=" + request);
+ }
+ }
+ }
+ });
+ }
+
+ private final void onConnectionFailed(final IMediaBrowserServiceCallbacks callback) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ Log.e(TAG, "onConnectFailed for " + mServiceComponent);
+
+ // Check to make sure there hasn't been a disconnect or a different
+ // ServiceConnection.
+ if (!isCurrent(callback, "onConnectFailed")) {
+ return;
+ }
+ // Don't allow them to call us twice.
+ if (mState != CONNECT_STATE_CONNECTING) {
+ Log.w(TAG, "onConnect from service while mState="
+ + getStateLabel(mState) + "... ignoring");
+ return;
+ }
+
+ // Clean up
+ forceCloseConnection();
+
+ // Tell the app.
+ mCallback.onConnectionFailed();
+ }
+ });
+ }
+
+ private final void onLoadChildren(final IMediaBrowserServiceCallbacks callback, final Uri uri,
+ final ParceledListSlice list) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // Check that there hasn't been a disconnect or a different
+ // ServiceConnection.
+ if (!isCurrent(callback, "onLoadChildren")) {
+ return;
+ }
+
+ List<MediaBrowserItem> data = list.getList();
+ if (DBG) {
+ Log.d(TAG, "onLoadChildren for " + mServiceComponent + " uri=" + uri);
+ }
+ if (data == null) {
+ data = Collections.emptyList();
+ }
+
+ // Check that the subscription is still subscribed.
+ final Subscription subscription = mSubscriptions.get(uri);
+ if (subscription == null) {
+ if (DBG) {
+ Log.d(TAG, "onLoadChildren for uri that isn't subscribed uri="
+ + uri);
+ }
+ return;
+ }
+
+ // Tell the app.
+ subscription.callback.onChildrenLoaded(uri, data);
+ }
+ });
+ }
+
+ private final void onLoadThumbnail(final IMediaBrowserServiceCallbacks callback,
+ final int seqNum, final Bitmap bitmap) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // Check that there hasn't been a disconnect or a different
+ // ServiceConnection.
+ if (!isCurrent(callback, "onLoadThumbnail")) {
+ return;
+ }
+
+ ThumbnailRequest request = mThumbnailRequests.get(seqNum);
+ if (request == null) {
+ Log.d(TAG, "onLoadThumbnail called for seqNum=" + seqNum + " request="
+ + request + " but the request is not registered");
+ return;
+ }
+ mThumbnailRequests.delete(seqNum);
+ for (ThumbnailCallback thumbnailCallback : request.getCallbacks()) {
+ thumbnailCallback.onThumbnailLoaded(request.mUri, bitmap);
+ }
+ }
+ });
+ }
+
+
+ /**
+ * Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not.
+ */
+ private boolean isCurrent(IMediaBrowserServiceCallbacks callback, String funcName) {
+ if (mServiceCallbacks != callback) {
+ if (mState != CONNECT_STATE_DISCONNECTED) {
+ Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection="
+ + mServiceCallbacks + " this=" + this);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ private ServiceCallbacks getNewServiceCallbacks() {
+ return new ServiceCallbacks(this);
+ }
+
+ /**
+ * Log internal state.
+ * @hide
+ */
+ void dump() {
+ Log.d(TAG, "MediaBrowser...");
+ Log.d(TAG, " mServiceComponent=" + mServiceComponent);
+ Log.d(TAG, " mCallback=" + mCallback);
+ Log.d(TAG, " mRootHints=" + mRootHints);
+ Log.d(TAG, " mState=" + getStateLabel(mState));
+ Log.d(TAG, " mServiceConnection=" + mServiceConnection);
+ Log.d(TAG, " mServiceBinder=" + mServiceBinder);
+ Log.d(TAG, " mServiceCallbacks=" + mServiceCallbacks);
+ Log.d(TAG, " mRootUri=" + mRootUri);
+ Log.d(TAG, " mMediaSessionToken=" + mMediaSessionToken);
+ }
+
+
+ /**
+ * Callbacks for connection related events.
+ */
+ public static class ConnectionCallback {
+ /**
+ * Invoked after {@link MediaBrowser#connect()} when the request has successfully completed.
+ */
+ public void onConnected() {
+ }
+
+ /**
+ * Invoked when the client is disconnected from the media browser.
+ */
+ public void onConnectionSuspended() {
+ }
+
+ /**
+ * Invoked when the connection to the media browser failed.
+ */
+ public void onConnectionFailed() {
+ }
+ }
+
+ /**
+ * Callbacks for subscription related events.
+ */
+ public static abstract class SubscriptionCallback {
+ /**
+ * Called when the list of children is loaded or updated.
+ */
+ public void onChildrenLoaded(@NonNull Uri parentUri,
+ @NonNull List<MediaBrowserItem> children) {
+ }
+
+ /**
+ * Called when the Uri doesn't exist or other errors in subscribing.
+ * <p>
+ * If this is called, the subscription remains until {@link MediaBrowser#unsubscribe}
+ * called, because some errors may heal themselves.
+ * </p>
+ */
+ public void onError(@NonNull Uri uri) {
+ }
+ }
+
+ /**
+ * Callbacks for thumbnail loading.
+ */
+ public static abstract class ThumbnailCallback {
+ /**
+ * Called when the thumbnail is loaded.
+ */
+ public void onThumbnailLoaded(@NonNull Uri uri, @NonNull Bitmap bitmap) {
+ }
+
+ /**
+ * Called when the Uri doesn’t exist or the bitmap cannot be loaded.
+ */
+ public void onError(@NonNull Uri uri) {
+ }
+ }
+
+ private static class ThumbnailRequest {
+ final int mSeq;
+ final Uri mUri;
+ final int mWidth;
+ final int mHeight;
+ final List<ThumbnailCallback> mCallbacks;
+
+ /**
+ * Constructs a thumbnail request.
+ * @param seq The unique sequence number assigned to the request by the media browser.
+ * @param uri The Uri for the thumbnail.
+ * @param width The width for the thumbnail.
+ * @param height The height for the thumbnail.
+ */
+ ThumbnailRequest(int seq, @NonNull Uri uri, int width, int height) {
+ if (uri == null) {
+ throw new IllegalArgumentException("thumbnail uri cannot be null");
+ }
+ this.mSeq = seq;
+ this.mUri = uri;
+ this.mWidth = width;
+ this.mHeight = height;
+ mCallbacks = new ArrayList<ThumbnailCallback>();
+ }
+
+ /**
+ * Adds a callback to the thumbnail request.
+ * If the callback already exists, it will not be added again.
+ */
+ public void addCallback(@NonNull ThumbnailCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null in ThumbnailRequest");
+ }
+ if (!mCallbacks.contains(callback)) {
+ mCallbacks.add(callback);
+ }
+ }
+
+ /**
+ * Checks if the thumbnail request has the same uri, width, and height as the given values.
+ */
+ public boolean isSameRequest(@Nullable Uri uri, int width, int height) {
+ return Objects.equals(mUri, uri) && mWidth == width && mHeight == height;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("ThumbnailRequest{");
+ sb.append("uri=").append(mUri);
+ sb.append(", width=").append(mWidth);
+ sb.append(", height=").append(mHeight);
+ sb.append(", seq=").append(mSeq);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ /**
+ * Gets an unmodifiable view of the list of callbacks associated with the request.
+ */
+ public List<ThumbnailCallback> getCallbacks() {
+ return Collections.unmodifiableList(mCallbacks);
+ }
+ }
+
+ /**
+ * ServiceConnection to the other app.
+ */
+ private class MediaServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ if (DBG) {
+ Log.d(TAG, "MediaServiceConnection.onServiceConnected name=" + name
+ + " binder=" + binder);
+ dump();
+ }
+
+ // Make sure we are still the current connection, and that they haven't called
+ // disconnect().
+ if (!isCurrent("onServiceConnected")) {
+ return;
+ }
+
+ // Save their binder
+ mServiceBinder = IMediaBrowserService.Stub.asInterface(binder);
+
+ // We make a new mServiceCallbacks each time we connect so that we can drop
+ // responses from previous connections.
+ mServiceCallbacks = getNewServiceCallbacks();
+
+ // Call connect, which is async. When we get a response from that we will
+ // say that we're connected.
+ try {
+ if (DBG) {
+ Log.d(TAG, "ServiceCallbacks.onConnect...");
+ dump();
+ }
+ mServiceBinder.connect(mContext.getPackageName(), mRootHints, mServiceCallbacks);
+ } catch (RemoteException ex) {
+ // Connect failed, which isn't good. But the auto-reconnect on the service
+ // will take over and we will come back. We will also get the
+ // onServiceDisconnected, which has all the cleanup code. So let that do it.
+ Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
+ if (DBG) {
+ Log.d(TAG, "ServiceCallbacks.onConnect...");
+ dump();
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DBG) {
+ Log.d(TAG, "MediaServiceConnection.onServiceDisconnected name=" + name
+ + " this=" + this + " mServiceConnection=" + mServiceConnection);
+ dump();
+ }
+
+ // Make sure we are still the current connection, and that they haven't called
+ // disconnect().
+ if (!isCurrent("onServiceDisconnected")) {
+ return;
+ }
+
+ // Clear out what we set in onServiceConnected
+ mServiceBinder = null;
+ mServiceCallbacks = null;
+
+ // And tell the app that it's suspended.
+ mState = CONNECT_STATE_SUSPENDED;
+ mCallback.onConnectionSuspended();
+ }
+
+ /**
+ * Return true if this is the current ServiceConnection. Also logs if it's not.
+ */
+ private boolean isCurrent(String funcName) {
+ if (mServiceConnection != this) {
+ if (mState != CONNECT_STATE_DISCONNECTED) {
+ // Check mState, because otherwise this log is noisy.
+ Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection="
+ + mServiceConnection + " this=" + this);
+ }
+ return false;
+ }
+ return true;
+ }
+ };
+
+ /**
+ * Callbacks from the service.
+ */
+ private static class ServiceCallbacks extends IMediaBrowserServiceCallbacks.Stub {
+ private WeakReference<MediaBrowser> mMediaBrowser;
+
+ public ServiceCallbacks(MediaBrowser mediaBrowser) {
+ mMediaBrowser = new WeakReference<MediaBrowser>(mediaBrowser);
+ }
+
+ /**
+ * The other side has acknowledged our connection. The parameters to this function
+ * are the initial data as requested.
+ */
+ @Override
+ public void onConnect(final Uri root, final MediaSession.Token session,
+ final Bundle extras) {
+ MediaBrowser mediaBrowser = mMediaBrowser.get();
+ if (mediaBrowser != null) {
+ mediaBrowser.onServiceConnected(this, root, session, extras);
+ }
+ }
+
+ /**
+ * The other side does not like us. Tell the app via onConnectionFailed.
+ */
+ @Override
+ public void onConnectFailed() {
+ MediaBrowser mediaBrowser = mMediaBrowser.get();
+ if (mediaBrowser != null) {
+ mediaBrowser.onConnectionFailed(this);
+ }
+ }
+
+ @Override
+ public void onLoadChildren(final Uri uri, final ParceledListSlice list) {
+ MediaBrowser mediaBrowser = mMediaBrowser.get();
+ if (mediaBrowser != null) {
+ mediaBrowser.onLoadChildren(this, uri, list);
+ }
+ }
+
+ @Override
+ public void onLoadThumbnail(final int seqNum, final Bitmap bitmap) {
+ MediaBrowser mediaBrowser = mMediaBrowser.get();
+ if (mediaBrowser != null) {
+ mediaBrowser.onLoadThumbnail(this, seqNum, bitmap);
+ }
+ }
+ }
+
+ private static class Subscription {
+ final Uri uri;
+ SubscriptionCallback callback;
+
+ Subscription(Uri u) {
+ this.uri = u;
+ }
+ }
+}
diff --git a/media/java/android/media/browse/MediaBrowserItem.java b/media/java/android/media/browse/MediaBrowserItem.java
new file mode 100644
index 0000000..d0a0342
--- /dev/null
+++ b/media/java/android/media/browse/MediaBrowserItem.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.browse;
+
+import android.annotation.DrawableRes;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.net.Uri;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Describes a media item in the list of items offered by a {@link MediaBrowserService}.
+ */
+public final class MediaBrowserItem implements Parcelable {
+ private final Uri mUri;
+ private final int mFlags;
+ private final CharSequence mTitle;
+ private final CharSequence mSummary;
+ private final Uri mIconUri;
+ private final int mIconResourceId;
+ private final Bundle mExtras;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
+ public @interface Flags { }
+
+ /**
+ * Flag: Indicates that the item has children of its own.
+ */
+ public static final int FLAG_BROWSABLE = 1 << 0;
+
+ /**
+ * Flag: Indicates that the item is playable.
+ * <p>
+ * The Uri of this item may be passed to link android.media.session.MediaController#play(Uri)
+ * to start playing it.
+ * </p>
+ */
+ public static final int FLAG_PLAYABLE = 1 << 1;
+
+ /**
+ * Initialize a MediaBrowserItem object.
+ */
+ private MediaBrowserItem(@NonNull Uri uri, int flags, @NonNull CharSequence title,
+ CharSequence summary, @Nullable Uri iconUri, int iconResourceId, Bundle extras) {
+ if (uri == null) {
+ throw new IllegalArgumentException("uri can not be null");
+ }
+ if (title == null) {
+ throw new IllegalArgumentException("title can not be null");
+ }
+ mUri = uri;
+ mFlags = flags;
+ mTitle = title;
+ mSummary = summary;
+ mIconUri = iconUri;
+ mIconResourceId = iconResourceId;
+ mExtras = extras;
+ }
+
+ /**
+ * Private constructor.
+ */
+ private MediaBrowserItem(Parcel in) {
+ mUri = Uri.CREATOR.createFromParcel(in);
+ mFlags = in.readInt();
+ mTitle = in.readCharSequence();
+ if (in.readInt() != 0) {
+ mSummary = in.readCharSequence();
+ } else {
+ mSummary = null;
+ }
+ if (in.readInt() != 0) {
+ mIconUri = Uri.CREATOR.createFromParcel(in);
+ } else {
+ mIconUri = null;
+ }
+ mIconResourceId = in.readInt();
+ if (in.readInt() != 0) {
+ mExtras = Bundle.CREATOR.createFromParcel(in);
+ } else {
+ mExtras = null;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ mUri.writeToParcel(out, flags);
+ out.writeInt(mFlags);
+ out.writeCharSequence(mTitle);
+ if (mSummary != null) {
+ out.writeInt(1);
+ out.writeCharSequence(mSummary);
+ } else {
+ out.writeInt(0);
+ }
+ if (mIconUri != null) {
+ out.writeInt(1);
+ mIconUri.writeToParcel(out, flags);
+ } else {
+ out.writeInt(0);
+ }
+ out.writeInt(mIconResourceId);
+ if (mExtras != null) {
+ out.writeInt(1);
+ mExtras.writeToParcel(out, flags);
+ } else {
+ out.writeInt(0);
+ }
+ }
+
+ public static final Parcelable.Creator<MediaBrowserItem> CREATOR =
+ new Parcelable.Creator<MediaBrowserItem>() {
+ @Override
+ public MediaBrowserItem createFromParcel(Parcel in) {
+ return new MediaBrowserItem(in);
+ }
+
+ @Override
+ public MediaBrowserItem[] newArray(int size) {
+ return new MediaBrowserItem[size];
+ }
+ };
+
+ /**
+ * Gets the Uri of the item.
+ */
+ public @NonNull Uri getUri() {
+ return mUri;
+ }
+
+ /**
+ * Gets the flags of the item.
+ */
+ public @Flags int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Returns whether this item is browsable.
+ * @see #FLAG_BROWSABLE
+ */
+ public boolean isBrowsable() {
+ return (mFlags & FLAG_BROWSABLE) != 0;
+ }
+
+ /**
+ * Returns whether this item is playable.
+ * @see #FLAG_PLAYABLE
+ */
+ public boolean isPlayable() {
+ return (mFlags & FLAG_PLAYABLE) != 0;
+ }
+
+ /**
+ * Gets the title of the item.
+ * @more
+ * The title will be shown as the first line of text when
+ * describing each item to the user.
+ */
+ public @NonNull CharSequence getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * Gets summary of the item, or null if none.
+ * @more
+ * The summary will be shown as the second line of text when
+ * describing each item to the user.
+ */
+ public @Nullable CharSequence getSummary() {
+ return mSummary;
+ }
+
+ /**
+ * Gets the Uri of the icon.
+ */
+ public @Nullable Uri getIconUri() {
+ return mIconUri;
+ }
+
+ /**
+ * Gets the resource id of the icon.
+ */
+ public @DrawableRes int getIconResourceId() {
+ return mIconResourceId;
+ }
+
+ /**
+ * Gets additional service-specified extras about the
+ * item or its content, or null if none.
+ */
+ public @Nullable Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Builder for {@link MediaBrowserItem} objects.
+ */
+ public static final class Builder {
+ private final Uri mUri;
+ private final int mFlags;
+ private final CharSequence mTitle;
+ private CharSequence mSummary;
+ private Uri mIconUri;
+ private int mIconResourceId;
+ private Bundle mExtras;
+
+ /**
+ * Creates an item builder.
+ */
+ public Builder(@NonNull Uri uri, @Flags int flags, @NonNull CharSequence title) {
+ if (uri == null) {
+ throw new IllegalArgumentException("uri can not be null");
+ }
+ if (title == null) {
+ throw new IllegalArgumentException("title can not be null");
+ }
+ mUri = uri;
+ mFlags = flags;
+ mTitle = title;
+ }
+
+ /**
+ * Sets summary of the item, or null if none.
+ */
+ public @NonNull Builder setSummary(@Nullable CharSequence summary) {
+ mSummary = summary;
+ return this;
+ }
+
+ /**
+ * Sets the uri of the icon.
+ * <p>
+ * Either {@link #setIconUri(Uri)} or {@link #setIconResourceId(int)} should be called.
+ * If both are specified, the resource id will be used to load the icon.
+ * </p>
+ */
+ public @NonNull Builder setIconUri(@Nullable Uri iconUri) {
+ mIconUri = iconUri;
+ return this;
+ }
+
+ /**
+ * Sets the resource id of the icon.
+ * <p>
+ * Either {@link #setIconUri(Uri)} or {@link #setIconResourceId(int)} should be specified.
+ * If both are specified, the resource id will be used to load the icon.
+ * </p>
+ */
+ public @NonNull Builder setIconResourceId(@DrawableRes int ResourceId) {
+ mIconResourceId = ResourceId;
+ return this;
+ }
+
+ /**
+ * Sets additional service-specified extras about the
+ * item or its content.
+ */
+ public @NonNull Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Builds the item.
+ */
+ public @NonNull MediaBrowserItem build() {
+ return new MediaBrowserItem(mUri, mFlags, mTitle, mSummary, mIconUri,
+ mIconResourceId, mExtras);
+ }
+ }
+}
+
diff --git a/media/java/android/media/browse/MediaBrowserService.java b/media/java/android/media/browse/MediaBrowserService.java
new file mode 100644
index 0000000..ffbaedd
--- /dev/null
+++ b/media/java/android/media/browse/MediaBrowserService.java
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.browse;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.media.session.MediaSession;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Base class for media browse services.
+ * <p>
+ * Media browse services enable applications to browse media content provided by an application
+ * and ask the application to start playing it. They may also be used to control content that
+ * is already playing by way of a {@link MediaSession}.
+ * </p>
+ *
+ * To extend this class, you must declare the service in your manifest file with
+ * an intent filter with the {@link #SERVICE_ACTION} action.
+ *
+ * For example:
+ * </p><pre>
+ * <service android:name=".MyMediaBrowserService"
+ * android:label="@string/service_name" >
+ * <intent-filter>
+ * <action android:name="android.media.browse.MediaBrowserService" />
+ * </intent-filter>
+ * </service>
+ * </pre>
+ *
+ */
+public abstract class MediaBrowserService extends Service {
+ private static final String TAG = "MediaBrowserService";
+ private static final boolean DBG = false;
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_ACTION = "android.media.browse.MediaBrowserService";
+
+ private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap();
+ private final Handler mHandler = new Handler();
+ private ServiceBinder mBinder;
+ MediaSession.Token mSession;
+
+ /**
+ * All the info about a connection.
+ */
+ private class ConnectionRecord {
+ String pkg;
+ Bundle rootHints;
+ IMediaBrowserServiceCallbacks callbacks;
+ BrowserRoot root;
+ HashSet<Uri> subscriptions = new HashSet();
+ }
+
+ /**
+ * Completion handler for asynchronous callback methods in {@link MediaBrowserService}.
+ * <p>
+ * Each of the methods that takes one of these to send the result must call
+ * {@link #sendResult} to respond to the caller with the given results. If those
+ * functions return without calling {@link #sendResult}, they must instead call
+ * {@link #detach} before returning, and then may call {@link #sendResult} when
+ * they are done. If more than one of those methods is called, an exception will
+ * be thrown.
+ *
+ * @see MediaBrowserService#onLoadChildren
+ * @see MediaBrowserService#onLoadThumbnail
+ */
+ public class Result<T> {
+ private Object mDebug;
+ private boolean mDetachCalled;
+ private boolean mSendResultCalled;
+
+ Result(Object debug) {
+ mDebug = debug;
+ }
+
+ /**
+ * Send the result back to the caller.
+ */
+ public void sendResult(T result) {
+ if (mSendResultCalled) {
+ throw new IllegalStateException("sendResult() called twice for: " + mDebug);
+ }
+ mSendResultCalled = true;
+ onResultSent(result);
+ }
+
+ /**
+ * Detach this message from the current thread and allow the {@link #sendResult}
+ * call to happen later.
+ */
+ public void detach() {
+ if (mDetachCalled) {
+ throw new IllegalStateException("detach() called when detach() had already"
+ + " been called for: " + mDebug);
+ }
+ if (mSendResultCalled) {
+ throw new IllegalStateException("detach() called when sendResult() had already"
+ + " been called for: " + mDebug);
+ }
+ mDetachCalled = true;
+ }
+
+ boolean isDone() {
+ return mDetachCalled || mSendResultCalled;
+ }
+
+ /**
+ * Called when the result is sent, after assertions about not being called twice
+ * have happened.
+ */
+ void onResultSent(T result) {
+ }
+ }
+
+ private class ServiceBinder extends IMediaBrowserService.Stub {
+ @Override
+ public void connect(final String pkg, final Bundle rootHints,
+ final IMediaBrowserServiceCallbacks callbacks) {
+
+ final int uid = Binder.getCallingUid();
+ if (!isValidPackage(pkg, uid)) {
+ throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
+ + " package=" + pkg);
+ }
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ final IBinder b = callbacks.asBinder();
+
+ // Clear out the old subscriptions. We are getting new ones.
+ mConnections.remove(b);
+
+ final ConnectionRecord connection = new ConnectionRecord();
+ connection.pkg = pkg;
+ connection.rootHints = rootHints;
+ connection.callbacks = callbacks;
+
+ connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
+
+ // If they didn't return something, don't allow this client.
+ if (connection.root == null) {
+ Log.i(TAG, "No root for client " + pkg + " from service "
+ + getClass().getName());
+ try {
+ callbacks.onConnectFailed();
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. "
+ + "pkg=" + pkg);
+ }
+ } else {
+ try {
+ mConnections.put(b, connection);
+ callbacks.onConnect(connection.root.getRootUri(),
+ mSession, connection.root.getExtras());
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Calling onConnect() failed. Dropping client. "
+ + "pkg=" + pkg);
+ mConnections.remove(b);
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ final IBinder b = callbacks.asBinder();
+
+ // Clear out the old subscriptions. We are getting new ones.
+ final ConnectionRecord old = mConnections.remove(b);
+ if (old != null) {
+ // TODO
+ }
+ }
+ });
+ }
+
+
+ @Override
+ public void addSubscription(final Uri uri, final IMediaBrowserServiceCallbacks callbacks) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ final IBinder b = callbacks.asBinder();
+
+ // Get the record for the connection
+ final ConnectionRecord connection = mConnections.get(b);
+ if (connection == null) {
+ Log.w(TAG, "addSubscription for callback that isn't registered uri="
+ + uri);
+ return;
+ }
+
+ MediaBrowserService.this.addSubscription(uri, connection);
+ }
+ });
+ }
+
+ @Override
+ public void removeSubscription(final Uri uri,
+ final IMediaBrowserServiceCallbacks callbacks) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ final IBinder b = callbacks.asBinder();
+
+ ConnectionRecord connection = mConnections.get(b);
+ if (connection == null) {
+ Log.w(TAG, "removeSubscription for callback that isn't registered uri="
+ + uri);
+ return;
+ }
+ if (!connection.subscriptions.remove(uri)) {
+ Log.w(TAG, "removeSubscription called for " + uri
+ + " which is not subscribed");
+ }
+ }
+ });
+ }
+
+ @Override
+ public void loadThumbnail(final int seq, final Uri uri, final int width, final int height,
+ final IMediaBrowserServiceCallbacks callbacks) {
+ if (uri == null) {
+ throw new IllegalStateException("loadThumbnail sent null list for uri " + uri);
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // In theory we could return a result to a new connection, but in practice
+ // the other side in MediaBrowser uses a new IMediaBrowserServiceCallbacks
+ // object every time it calls connect(), so as long as it does that we won't
+ // see results sent for the wrong connection.
+ final ConnectionRecord connection = mConnections.get(callbacks.asBinder());
+ if (connection == null) {
+ if (DBG) {
+ Log.d(TAG, "Not loading bitmap for invalid connection. uri=" + uri);
+ }
+ return;
+ }
+
+ final Result<Bitmap> result = new Result<Bitmap>(uri) {
+ @Override
+ void onResultSent(Bitmap bitmap) {
+ if (mConnections.get(connection.callbacks.asBinder()) != connection) {
+ if (DBG) {
+ Log.d(TAG, "Not sending onLoadThumbnail result for connection"
+ + " that has been disconnected. pkg=" + connection.pkg
+ + " uri=" + uri);
+ }
+ return;
+ }
+
+ try {
+ callbacks.onLoadThumbnail(seq, bitmap);
+ } catch (RemoteException e) {
+ // The other side is in the process of crashing.
+ Log.w(TAG, "RemoteException in calling onLoadThumbnail", e);
+ }
+ }
+ };
+
+ onLoadThumbnail(uri, width, height, result);
+
+ if (!result.isDone()) {
+ throw new IllegalStateException("onLoadThumbnail must call detach() or"
+ + " sendResult() before returning for package=" + connection.pkg
+ + " uri=" + uri);
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mBinder = new ServiceBinder();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (SERVICE_ACTION.equals(intent.getAction())) {
+ return mBinder;
+ }
+ return null;
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ }
+
+ /**
+ * Called to get the root information for browsing by a particular client.
+ * <p>
+ * The implementation should verify that the client package has
+ * permission to access browse media information before returning
+ * the root uri; it should return null if the client is not
+ * allowed to access this information.
+ * </p>
+ *
+ * @param clientPackageName The package name of the application
+ * which is requesting access to browse media.
+ * @param clientUid The uid of the application which is requesting
+ * access to browse media.
+ * @param rootHints An optional bundle of service-specific arguments to send
+ * to the media browse service when connecting and retrieving the root uri
+ * for browsing, or null if none. The contents of this bundle may affect
+ * the information returned when browsing.
+ */
+ public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
+ int clientUid, @Nullable Bundle rootHints);
+
+ /**
+ * Called to get information about the children of a media item.
+ * <p>
+ * Implementations must call result.{@link Result#sendResult result.sendResult} with the list
+ * of children. If loading the children will be an expensive operation that should be performed
+ * on another thread, result.{@link Result#detach result.detach} may be called before returning
+ * from this function, and then {@link Result#sendResult result.sendResult} called when
+ * the loading is complete.
+ *
+ * @param parentUri The uri of the parent media item whose
+ * children are to be queried.
+ * @return The list of children, or null if the uri is invalid.
+ */
+ protected abstract void onLoadChildren(@NonNull Uri parentUri,
+ @NonNull Result<List<MediaBrowserItem>> result);
+
+ /**
+ * Called to get the thumbnail of a particular media item.
+ * <p>
+ * Implementations must call result.{@link Result#sendResult result.sendResult} with the bitmap.
+ * If loading the bitmap will be an expensive operation that should be performed
+ * on another thread, result.{@link Result#detach result.detach} may be called before returning
+ * from this function, and then {@link Result#sendResult result.sendResult} called when
+ * the loading is complete.
+ *
+ * @param uri The uri of the media item.
+ * @param width The requested width of the icon in dp.
+ * @param height The requested height of the icon in dp.
+ *
+ * @return The file descriptor of the thumbnail, which may then be loaded
+ * using a bitmap factory, or null if the item does not have a thumbnail.
+ */
+ protected abstract void onLoadThumbnail(@NonNull Uri uri, int width, int height,
+ @NonNull Result<Bitmap> result);
+
+ /**
+ * Call to set the media session.
+ * <p>
+ * This must be called before onCreate returns.
+ *
+ * @return The media session token, must not be null.
+ */
+ public void setSessionToken(MediaSession.Token token) {
+ if (token == null) {
+ throw new IllegalStateException(this.getClass().getName()
+ + ".onCreateSession() set invalid MediaSession.Token");
+ }
+ mSession = token;
+ }
+
+ /**
+ * Gets the session token, or null if it has not yet been created
+ * or if it has been destroyed.
+ */
+ public @Nullable MediaSession.Token getSessionToken() {
+ return mSession;
+ }
+
+ /**
+ * Notifies all connected media browsers that the children of
+ * the specified Uri have changed in some way.
+ * This will cause browsers to fetch subscribed content again.
+ *
+ * @param parentUri The uri of the parent media item whose
+ * children changed.
+ */
+ public void notifyChildrenChanged(@NonNull final Uri parentUri) {
+ if (parentUri == null) {
+ throw new IllegalArgumentException("parentUri cannot be null in notifyChildrenChanged");
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ for (IBinder binder : mConnections.keySet()) {
+ ConnectionRecord connection = mConnections.get(binder);
+ if (connection.subscriptions.contains(parentUri)) {
+ performLoadChildren(parentUri, connection);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Return whether the given package is one of the ones that is owned by the uid.
+ */
+ private boolean isValidPackage(String pkg, int uid) {
+ if (pkg == null) {
+ return false;
+ }
+ final PackageManager pm = getPackageManager();
+ final String[] packages = pm.getPackagesForUid(uid);
+ final int N = packages.length;
+ for (int i=0; i<N; i++) {
+ if (packages[i].equals(pkg)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Save the subscription and if it is a new subscription send the results.
+ */
+ private void addSubscription(Uri uri, ConnectionRecord connection) {
+ // Save the subscription
+ final boolean added = connection.subscriptions.add(uri);
+
+ // If this is a new subscription, send the results
+ if (added) {
+ performLoadChildren(uri, connection);
+ }
+ }
+
+ /**
+ * Call onLoadChildren and then send the results back to the connection.
+ * <p>
+ * Callers must make sure that this connection is still connected.
+ */
+ private void performLoadChildren(final Uri uri, final ConnectionRecord connection) {
+ final Result<List<MediaBrowserItem>> result = new Result<List<MediaBrowserItem>>(uri) {
+ @Override
+ void onResultSent(List<MediaBrowserItem> list) {
+ if (list == null) {
+ throw new IllegalStateException("onLoadChildren sent null list for uri " + uri);
+ }
+ if (mConnections.get(connection.callbacks.asBinder()) != connection) {
+ if (DBG) {
+ Log.d(TAG, "Not sending onLoadChildren result for connection that has"
+ + " been disconnected. pkg=" + connection.pkg + " uri=" + uri);
+ }
+ return;
+ }
+
+ final ParceledListSlice<MediaBrowserItem> pls = new ParceledListSlice(list);
+ try {
+ connection.callbacks.onLoadChildren(uri, pls);
+ } catch (RemoteException ex) {
+ // The other side is in the process of crashing.
+ Log.w(TAG, "Calling onLoadChildren() failed for uri=" + uri
+ + " package=" + connection.pkg);
+ }
+ }
+ };
+
+ onLoadChildren(uri, result);
+
+ if (!result.isDone()) {
+ throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
+ + " before returning for package=" + connection.pkg + " uri=" + uri);
+ }
+ }
+
+ /**
+ * Contains information that the browser service needs to send to the client
+ * when first connected.
+ */
+ public static final class BrowserRoot {
+ final private Uri mUri;
+ final private Bundle mExtras;
+
+ /**
+ * Constructs a browser root.
+ * @param uri The root Uri for browsing.
+ * @param extras Any extras about the browser service.
+ */
+ public BrowserRoot(@NonNull Uri uri, @Nullable Bundle extras) {
+ if (uri == null) {
+ throw new IllegalArgumentException("The root uri in BrowserRoot cannot be null. " +
+ "Use null for BrowserRoot instead.");
+ }
+ mUri = uri;
+ mExtras = extras;
+ }
+
+ /**
+ * Gets the root uri for browsing.
+ */
+ public Uri getRootUri() {
+ return mUri;
+ }
+
+ /**
+ * Gets any extras about the brwoser service.
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+ }
+}
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index a92350b..2c190b78 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -16,10 +16,13 @@
package android.media.session;
import android.content.ComponentName;
+import android.content.pm.ParceledListSlice;
+import android.media.AudioAttributes;
import android.media.MediaMetadata;
import android.media.routing.IMediaRouter;
import android.media.session.ISessionController;
import android.media.session.PlaybackState;
+import android.media.session.MediaSession;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -39,9 +42,13 @@
// These commands are for the TransportPerformer
void setMetadata(in MediaMetadata metadata);
void setPlaybackState(in PlaybackState state);
+ void setQueue(in ParceledListSlice queue);
+ void setQueueTitle(CharSequence title);
+ void setExtras(in Bundle extras);
void setRatingType(int type);
// These commands relate to volume handling
- void configureVolumeHandling(int type, int arg1, int arg2);
+ void setPlaybackToLocal(in AudioAttributes attributes);
+ void setPlaybackToRemote(int control, int max);
void setCurrentVolume(int currentVolume);
}
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 39391b6..9911129 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -17,6 +17,7 @@
import android.media.Rating;
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -24,11 +25,14 @@
* @hide
*/
oneway interface ISessionCallback {
- void onCommand(String command, in Bundle extras, in ResultReceiver cb);
+ void onCommand(String command, in Bundle args, in ResultReceiver cb);
void onMediaButton(in Intent mediaButtonIntent, int sequenceNumber, in ResultReceiver cb);
// These callbacks are for the TransportPerformer
void onPlay();
+ void onPlayUri(in Uri uri, in Bundle extras);
+ void onPlayFromSearch(String query, in Bundle extras);
+ void onSkipToTrack(long id);
void onPause();
void onStop();
void onNext();
@@ -37,6 +41,7 @@
void onRewind();
void onSeekTo(long pos);
void onRate(in Rating rating);
+ void onCustomAction(String action, in Bundle args);
// These callbacks are for volume handling
void onAdjustVolume(int direction);
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index b555220..d20b0ad5 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -16,6 +16,7 @@
package android.media.session;
import android.content.Intent;
+import android.content.pm.ParceledListSlice;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.routing.IMediaRouterDelegate;
@@ -24,16 +25,20 @@
import android.media.session.MediaSessionInfo;
import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
+import android.media.session.MediaSession;
+import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.view.KeyEvent;
+import java.util.List;
+
/**
* Interface to a MediaSession in the system.
* @hide
*/
interface ISessionController {
- void sendCommand(String command, in Bundle extras, in ResultReceiver cb);
+ void sendCommand(String command, in Bundle args, in ResultReceiver cb);
boolean sendMediaButton(in KeyEvent mediaButton);
void registerCallbackListener(in ISessionControllerCallback cb);
void unregisterCallbackListener(in ISessionControllerCallback cb);
@@ -48,6 +53,9 @@
// These commands are for the TransportControls
void play();
+ void playUri(in Uri uri, in Bundle extras);
+ void playFromSearch(String string, in Bundle extras);
+ void skipToTrack(long id);
void pause();
void stop();
void next();
@@ -56,7 +64,11 @@
void rewind();
void seekTo(long pos);
void rate(in Rating rating);
+ void sendCustomAction(String action, in Bundle args);
MediaMetadata getMetadata();
PlaybackState getPlaybackState();
+ ParceledListSlice getQueue();
+ CharSequence getQueueTitle();
+ Bundle getExtras();
int getRatingType();
}
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index 64d2bc7..78cd699 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -15,9 +15,11 @@
package android.media.session;
+import android.content.pm.ParceledListSlice;
import android.media.MediaMetadata;
import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
+import android.media.session.MediaSession;
import android.os.Bundle;
/**
@@ -29,5 +31,8 @@
// These callbacks are for the TransportController
void onPlaybackStateChanged(in PlaybackState state);
void onMetadataChanged(in MediaMetadata metadata);
+ void onQueueChanged(in ParceledListSlice queue);
+ void onQueueTitleChanged(CharSequence title);
+ void onExtrasChanged(in Bundle extras);
void onVolumeInfoChanged(in ParcelableVolumeInfo info);
}
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 7fedd82..050db21 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -18,11 +18,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ParceledListSlice;
+import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
import android.media.routing.MediaRouter;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -35,6 +38,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.List;
/**
* Allows an app to interact with an ongoing media session. Media buttons and
@@ -54,6 +58,9 @@
private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
private static final int MSG_UPDATE_METADATA = 3;
private static final int MSG_UPDATE_VOLUME = 4;
+ private static final int MSG_UPDATE_QUEUE = 5;
+ private static final int MSG_UPDATE_QUEUE_TITLE = 6;
+ private static final int MSG_UPDATE_EXTRAS = 7;
private final ISessionController mSessionBinder;
@@ -161,6 +168,23 @@
}
/**
+ * Get the current play queue for this session.
+ *
+ * @return The current play queue or null.
+ */
+ public @Nullable List<MediaSession.Track> getQueue() {
+ try {
+ ParceledListSlice queue = mSessionBinder.getQueue();
+ if (queue != null) {
+ return queue.getList();
+ }
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling getQueue.", e);
+ }
+ return null;
+ }
+
+ /**
* Get the rating type supported by the session. One of:
* <ul>
* <li>{@link Rating#RATING_NONE}</li>
@@ -206,7 +230,7 @@
public @Nullable VolumeInfo getVolumeInfo() {
try {
ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
- return new VolumeInfo(result.volumeType, result.audioStream, result.controlType,
+ return new VolumeInfo(result.volumeType, result.audioAttrs, result.controlType,
result.maxVolume, result.currentVolume);
} catch (RemoteException e) {
@@ -216,8 +240,8 @@
}
/**
- * Set the volume of the stream or output this session is playing on. The
- * command will be ignored if it does not support
+ * Set the volume of the output this session is playing on. The command will
+ * be ignored if it does not support
* {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
* {@link AudioManager} may be used to affect the handling.
*
@@ -234,8 +258,8 @@
}
/**
- * Adjust the volume of the stream or output this session is playing on. The
- * direction must be one of {@link AudioManager#ADJUST_LOWER},
+ * Adjust the volume of the output this session is playing on. The direction
+ * must be one of {@link AudioManager#ADJUST_LOWER},
* {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
* The command will be ignored if the session does not support
* {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
@@ -305,16 +329,16 @@
* commands should only be sent to sessions that the controller owns.
*
* @param command The command to send
- * @param params Any parameters to include with the command
+ * @param args Any parameters to include with the command
* @param cb The callback to receive the result on
*/
- public void sendControlCommand(@NonNull String command, @Nullable Bundle params,
+ public void sendCommand(@NonNull String command, @Nullable Bundle args,
@Nullable ResultReceiver cb) {
if (TextUtils.isEmpty(command)) {
throw new IllegalArgumentException("command cannot be null or empty");
}
try {
- mSessionBinder.sendCommand(command, params, cb);
+ mSessionBinder.sendCommand(command, args, cb);
} catch (RemoteException e) {
Log.d(TAG, "Dead object in sendCommand.", e);
}
@@ -437,6 +461,34 @@
}
/**
+ * Override to handle changes to tracks in the queue.
+ *
+ * @param queue A list of tracks in the current play queue. It should include the currently
+ * playing track as well as previous and upcoming tracks if applicable.
+ * @see MediaSession.Track
+ */
+ public void onQueueChanged(@Nullable List<MediaSession.Track> queue) {
+ }
+
+ /**
+ * Override to handle changes to the queue title.
+ *
+ * @param title The title that should be displayed along with the play queue such as
+ * "Now Playing". May be null if there is no such title.
+ */
+ public void onQueueTitleChanged(@Nullable CharSequence title) {
+ }
+
+ /**
+ * Override to handle changes to the {@link MediaSession} extras.
+ *
+ * @param extras The extras that can include other information associated with the
+ * {@link MediaSession}.
+ */
+ public void onExtrasChanged(@Nullable Bundle extras) {
+ }
+
+ /**
* Override to handle changes to the volume info.
*
* @param info The current volume info for this session.
@@ -467,6 +519,54 @@
}
/**
+ * Request that the player start playback for a specific {@link Uri}.
+ *
+ * @param uri The uri of the requested media.
+ * @param extras Optional extras that can include extra information about the media item
+ * to be played.
+ */
+ public void playUri(Uri uri, Bundle extras) {
+ if (uri == null) {
+ throw new IllegalArgumentException("You must specify a non-null Uri for playUri.");
+ }
+ try {
+ mSessionBinder.playUri(uri, extras);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling play(" + uri + ").", e);
+ }
+ }
+
+ /**
+ * Request that the player start playback for a specific search query.
+ *
+ * @param query The search query.
+ * @param extras Optional extras that can include extra information about the query.
+ */
+ public void playFromSearch(String query, Bundle extras) {
+ if (TextUtils.isEmpty(query)) {
+ throw new IllegalArgumentException(
+ "You must specify a non-empty search query for playFromSearch.");
+ }
+ try {
+ mSessionBinder.playFromSearch(query, extras);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling play(" + query + ").", e);
+ }
+ }
+
+ /**
+ * Play a track with a specific id in the play queue.
+ * If you specify an id that is not in the play queue, the behavior is undefined.
+ */
+ public void skipToTrack(long id) {
+ try {
+ mSessionBinder.skipToTrack(id);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling skipToTrack(" + id + ").", e);
+ }
+ }
+
+ /**
* Request that the player pause its playback and stay at its current
* position.
*/
@@ -563,6 +663,41 @@
Log.wtf(TAG, "Error calling rate.", e);
}
}
+
+ /**
+ * Send a custom action back for the {@link MediaSession} to perform.
+ *
+ * @param customAction The action to perform.
+ * @param args Optional arguments to supply to the {@link MediaSession} for this
+ * custom action.
+ */
+ public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction,
+ @Nullable Bundle args) {
+ if (customAction == null) {
+ throw new IllegalArgumentException("CustomAction cannot be null.");
+ }
+ sendCustomAction(customAction.getAction(), args);
+ }
+
+ /**
+ * Send the id and args from a custom action back for the {@link MediaSession} to perform.
+ *
+ * @see #sendCustomAction(PlaybackState.CustomAction action, Bundle args)
+ * @param action The action identifier of the {@link PlaybackState.CustomAction} as
+ * specified by the {@link MediaSession}.
+ * @param args Optional arguments to supply to the {@link MediaSession} for this
+ * custom action.
+ */
+ public void sendCustomAction(@NonNull String action, @Nullable Bundle args) {
+ if (TextUtils.isEmpty(action)) {
+ throw new IllegalArgumentException("CustomAction cannot be null.");
+ }
+ try {
+ mSessionBinder.sendCustomAction(action, args);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Dead object in sendCustomAction.", e);
+ }
+ }
}
/**
@@ -570,17 +705,17 @@
*/
public static final class VolumeInfo {
private final int mVolumeType;
- private final int mAudioStream;
private final int mVolumeControl;
private final int mMaxVolume;
private final int mCurrentVolume;
+ private final AudioAttributes mAudioAttrs;
/**
* @hide
*/
- public VolumeInfo(int type, int stream, int control, int max, int current) {
+ public VolumeInfo(int type, AudioAttributes attrs, int control, int max, int current) {
mVolumeType = type;
- mAudioStream = stream;
+ mAudioAttrs = attrs;
mVolumeControl = control;
mMaxVolume = max;
mCurrentVolume = current;
@@ -600,14 +735,15 @@
}
/**
- * Get the stream this is currently controlling volume on. When the volume
- * type is {@link MediaSession#PLAYBACK_TYPE_REMOTE} this value does not
- * have meaning and should be ignored.
+ * Get the audio attributes for this session. The attributes will affect
+ * volume handling for the session. When the volume type is
+ * {@link MediaSession#PLAYBACK_TYPE_REMOTE} these may be ignored by the
+ * remote volume handler.
*
- * @return The stream this session is playing on.
+ * @return The attributes for this session.
*/
- public int getAudioStream() {
- return mAudioStream;
+ public AudioAttributes getAudioAttributes() {
+ return mAudioAttrs;
}
/**
@@ -676,10 +812,35 @@
}
@Override
+ public void onQueueChanged(ParceledListSlice parceledQueue) {
+ List<MediaSession.Track> queue = parceledQueue.getList();
+ MediaController controller = mController.get();
+ if (controller != null) {
+ controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
+ }
+ }
+
+ @Override
+ public void onQueueTitleChanged(CharSequence title) {
+ MediaController controller = mController.get();
+ if (controller != null) {
+ controller.postMessage(MSG_UPDATE_QUEUE_TITLE, title, null);
+ }
+ }
+
+ @Override
+ public void onExtrasChanged(Bundle extras) {
+ MediaController controller = mController.get();
+ if (controller != null) {
+ controller.postMessage(MSG_UPDATE_EXTRAS, extras, null);
+ }
+ }
+
+ @Override
public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
MediaController controller = mController.get();
if (controller != null) {
- VolumeInfo info = new VolumeInfo(pvi.volumeType, pvi.audioStream, pvi.controlType,
+ VolumeInfo info = new VolumeInfo(pvi.volumeType, pvi.audioAttrs, pvi.controlType,
pvi.maxVolume, pvi.currentVolume);
controller.postMessage(MSG_UPDATE_VOLUME, info, null);
}
@@ -707,6 +868,15 @@
case MSG_UPDATE_METADATA:
mCallback.onMetadataChanged((MediaMetadata) msg.obj);
break;
+ case MSG_UPDATE_QUEUE:
+ mCallback.onQueueChanged((List<MediaSession.Track>) msg.obj);
+ break;
+ case MSG_UPDATE_QUEUE_TITLE:
+ mCallback.onQueueTitleChanged((CharSequence) msg.obj);
+ break;
+ case MSG_UPDATE_EXTRAS:
+ mCallback.onExtrasChanged((Bundle) msg.obj);
+ break;
case MSG_UPDATE_VOLUME:
mCallback.onVolumeInfoChanged((VolumeInfo) msg.obj);
break;
diff --git a/media/java/android/media/session/MediaSession.aidl b/media/java/android/media/session/MediaSession.aidl
index 4a7efc2..0ad58c4 100644
--- a/media/java/android/media/session/MediaSession.aidl
+++ b/media/java/android/media/session/MediaSession.aidl
@@ -16,3 +16,4 @@
package android.media.session;
parcelable MediaSession.Token;
+parcelable MediaSession.Track;
\ No newline at end of file
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 086cd23..be2d479 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -22,14 +22,14 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ParceledListSlice;
+import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
import android.media.routing.MediaRouter;
-import android.media.session.ISessionController;
-import android.media.session.ISession;
-import android.media.session.ISessionCallback;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -40,11 +40,8 @@
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.Log;
-import com.android.internal.telephony.DctConstants.Activity;
-
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -268,18 +265,22 @@
}
/**
- * Set the stream this session is playing on. This will affect the system's
- * volume handling for this session. If {@link #setPlaybackToRemote} was
- * previously called it will stop receiving volume commands and the system
- * will begin sending volume changes to the appropriate stream.
+ * Set the attributes for this session's audio. This will affect the
+ * system's volume handling for this session. If
+ * {@link #setPlaybackToRemote} was previously called it will stop receiving
+ * volume commands and the system will begin sending volume changes to the
+ * appropriate stream.
* <p>
- * By default sessions are on {@link AudioManager#STREAM_MUSIC}.
+ * By default sessions use attributes for media.
*
- * @param stream The {@link AudioManager} stream this session is playing on.
+ * @param attributes The {@link AudioAttributes} for this session's audio.
*/
- public void setPlaybackToLocal(int stream) {
+ public void setPlaybackToLocal(AudioAttributes attributes) {
+ if (attributes == null) {
+ throw new IllegalArgumentException("Attributes cannot be null for local playback.");
+ }
try {
- mBinder.configureVolumeHandling(PLAYBACK_TYPE_LOCAL, stream, 0);
+ mBinder.setPlaybackToLocal(attributes);
} catch (RemoteException e) {
Log.wtf(TAG, "Failure in setPlaybackToLocal.", e);
}
@@ -288,9 +289,10 @@
/**
* Configure this session to use remote volume handling. This must be called
* to receive volume button events, otherwise the system will adjust the
- * current stream volume for this session. If {@link #setPlaybackToLocal}
- * was previously called that stream will stop receiving volume changes for
- * this session.
+ * appropriate stream volume for this session. If
+ * {@link #setPlaybackToLocal} was previously called the system will stop
+ * handling volume changes for this session and pass them to the volume
+ * provider instead.
*
* @param volumeProvider The provider that will handle volume changes. May
* not be null.
@@ -308,7 +310,7 @@
});
try {
- mBinder.configureVolumeHandling(PLAYBACK_TYPE_REMOTE, volumeProvider.getVolumeControl(),
+ mBinder.setPlaybackToRemote(volumeProvider.getVolumeControl(),
volumeProvider.getMaxVolume());
} catch (RemoteException e) {
Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
@@ -468,6 +470,54 @@
}
/**
+ * Update the list of tracks in the play queue. It is an ordered list and should contain the
+ * current track, and previous or upcoming tracks if they exist.
+ * Specify null if there is no current play queue.
+ * <p>
+ * The queue should be of reasonable size. If the play queue is unbounded within your
+ * app, it is better to send a reasonable amount in a sliding window instead.
+ *
+ * @param queue A list of tracks in the play queue.
+ */
+ public void setQueue(@Nullable List<Track> queue) {
+ try {
+ mBinder.setQueue(new ParceledListSlice<Track>(queue));
+ } catch (RemoteException e) {
+ Log.wtf("Dead object in setQueue.", e);
+ }
+ }
+
+ /**
+ * Set the title of the play queue. The UI should display this title along
+ * with the play queue itself.
+ * e.g. "Play Queue", "Now Playing", or an album name.
+ *
+ * @param title The title of the play queue.
+ */
+ public void setQueueTitle(@Nullable CharSequence title) {
+ try {
+ mBinder.setQueueTitle(title);
+ } catch (RemoteException e) {
+ Log.wtf("Dead object in setQueueTitle.", e);
+ }
+ }
+
+ /**
+ * Set some extras that can be associated with the {@link MediaSession}. No assumptions should
+ * be made as to how a {@link MediaController} will handle these extras.
+ * Keys should be fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts.
+ *
+ * @param extras The extras associated with the {@link MediaSession}.
+ */
+ public void setExtras(@Nullable Bundle extras) {
+ try {
+ mBinder.setExtras(extras);
+ } catch (RemoteException e) {
+ Log.wtf("Dead object in setExtras.", e);
+ }
+ }
+
+ /**
* Notify the system that the remote volume changed.
*
* @param provider The provider that is handling volume changes.
@@ -489,6 +539,18 @@
postToTransportCallbacks(TransportMessageHandler.MSG_PLAY);
}
+ private void dispatchPlayUri(Uri uri, Bundle extras) {
+ postToTransportCallbacks(TransportMessageHandler.MSG_PLAY_URI, uri, extras);
+ }
+
+ private void dispatchPlayFromSearch(String query, Bundle extras) {
+ postToTransportCallbacks(TransportMessageHandler.MSG_PLAY_SEARCH, query, extras);
+ }
+
+ private void dispatchSkipToTrack(long id) {
+ postToTransportCallbacks(TransportMessageHandler.MSG_SKIP_TO_TRACK, id);
+ }
+
private void dispatchPause() {
postToTransportCallbacks(TransportMessageHandler.MSG_PAUSE);
}
@@ -521,6 +583,10 @@
postToTransportCallbacks(TransportMessageHandler.MSG_RATE, rating);
}
+ private void dispatchCustomAction(String action, Bundle args) {
+ postToTransportCallbacks(TransportMessageHandler.MSG_CUSTOM_ACTION, action, args);
+ }
+
private TransportMessageHandler getTransportControlsHandlerForCallbackLocked(
TransportControlsCallback callback) {
for (int i = mTransportCallbacks.size() - 1; i >= 0; i--) {
@@ -550,6 +616,14 @@
}
}
+ private void postToTransportCallbacks(int what, Object obj, Bundle args) {
+ synchronized (mLock) {
+ for (int i = mTransportCallbacks.size() - 1; i >= 0; i--) {
+ mTransportCallbacks.get(i).post(what, obj, args);
+ }
+ }
+ }
+
private void postToTransportCallbacks(int what) {
postToTransportCallbacks(what, null);
}
@@ -581,8 +655,8 @@
return false;
}
- private void postCommand(String command, Bundle extras, ResultReceiver resultCb) {
- Command cmd = new Command(command, extras, resultCb);
+ private void postCommand(String command, Bundle args, ResultReceiver resultCb) {
+ Command cmd = new Command(command, args, resultCb);
synchronized (mLock) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
mCallbacks.get(i).post(CallbackMessageHandler.MSG_COMMAND, cmd);
@@ -685,15 +759,15 @@
}
/**
- * Called when a controller has sent a custom command to this session.
+ * Called when a controller has sent a command to this session.
* The owner of the session may handle custom commands but is not
* required to.
*
* @param command The command name.
- * @param extras Optional parameters for the command, may be null.
+ * @param args Optional parameters for the command, may be null.
* @param cb A result receiver to which a result may be sent by the command, may be null.
*/
- public void onControlCommand(@NonNull String command, @Nullable Bundle extras,
+ public void onCommand(@NonNull String command, @Nullable Bundle args,
@Nullable ResultReceiver cb) {
}
}
@@ -711,6 +785,24 @@
}
/**
+ * Override to handle requests to play a specific {@link Uri}.
+ */
+ public void onPlayUri(Uri uri, Bundle extras) {
+ }
+
+ /**
+ * Override to handle requests to begin playback from a search query.
+ */
+ public void onPlayFromSearch(String query, Bundle extras) {
+ }
+
+ /**
+ * Override to handle requests to play a track with a given id from the play queue.
+ */
+ public void onSkipToTrack(long id) {
+ }
+
+ /**
* Override to handle requests to pause playback.
*/
public void onPause() {
@@ -761,6 +853,17 @@
*/
public void onSetRating(@NonNull Rating rating) {
}
+
+ /**
+ * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be
+ * performed.
+ *
+ * @param action The action that was originally sent in the
+ * {@link PlaybackState.CustomAction}.
+ * @param extras Optional extras specified by the {@link MediaController}.
+ */
+ public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
+ }
}
/**
@@ -774,10 +877,10 @@
}
@Override
- public void onCommand(String command, Bundle extras, ResultReceiver cb) {
+ public void onCommand(String command, Bundle args, ResultReceiver cb) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.postCommand(command, extras, cb);
+ session.postCommand(command, args, cb);
}
}
@@ -805,6 +908,30 @@
}
@Override
+ public void onPlayUri(Uri uri, Bundle extras) {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.dispatchPlayUri(uri, extras);
+ }
+ }
+
+ @Override
+ public void onPlayFromSearch(String query, Bundle extras) {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.dispatchPlayFromSearch(query, extras);
+ }
+ }
+
+ @Override
+ public void onSkipToTrack(long id) {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.dispatchSkipToTrack(id);
+ }
+ }
+
+ @Override
public void onPause() {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -869,6 +996,14 @@
}
@Override
+ public void onCustomAction(String action, Bundle args) {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.dispatchCustomAction(action, args);
+ }
+ }
+
+ @Override
public void onAdjustVolume(int direction) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -890,6 +1025,157 @@
}
+ /**
+ * A single track that is part of the play queue. It contains information necessary to display
+ * a single track in the queue.
+ */
+ public static final class Track implements Parcelable {
+ /**
+ * This id is reserved. No tracks can be explicitly asigned this id.
+ */
+ public static final int UNKNOWN_ID = -1;
+
+ private final MediaMetadata mMetadata;
+ private final long mId;
+ private final Uri mUri;
+ private final Bundle mExtras;
+
+ /**
+ * Create a new {@link MediaSession.Track}.
+ *
+ * @param metadata The metadata for this track.
+ * @param id An identifier for this track. It must be unique within the play queue.
+ * @param uri The uri for this track.
+ * @param extras A bundle of extras that can be used to add extra information about the
+ * track.
+ */
+ private Track(MediaMetadata metadata, long id, Uri uri, Bundle extras) {
+ mMetadata = metadata;
+ mId = id;
+ mUri = uri;
+ mExtras = extras;
+ }
+
+ private Track(Parcel in) {
+ mMetadata = MediaMetadata.CREATOR.createFromParcel(in);
+ mId = in.readLong();
+ mUri = Uri.CREATOR.createFromParcel(in);
+ mExtras = in.readBundle();
+ }
+
+ /**
+ * Get the metadata for this track.
+ */
+ public MediaMetadata getMetadata() {
+ return mMetadata;
+ }
+
+ /**
+ * Get the id for this track.
+ */
+ public long getId() {
+ return mId;
+ }
+
+ /**
+ * Get the Uri for this track.
+ */
+ public Uri getUri() {
+ return mUri;
+ }
+
+ /**
+ * Get the extras for this track.
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Builder for {@link MediaSession.Track} objects.
+ */
+ public static final class Builder {
+ private final MediaMetadata mMetadata;
+ private final long mId;
+ private final Uri mUri;
+
+ private Bundle mExtras;
+
+ /**
+ * Create a builder with the metadata, id, and uri already set.
+ */
+ public Builder(MediaMetadata metadata, long id, Uri uri) {
+ if (metadata == null) {
+ throw new IllegalArgumentException(
+ "You must specify a non-null MediaMetadata to build a Track.");
+ }
+ if (uri == null) {
+ throw new IllegalArgumentException(
+ "You must specify a non-null Uri to build a Track.");
+ }
+ if (id == UNKNOWN_ID) {
+ throw new IllegalArgumentException(
+ "You must specify an id other than UNKNOWN_ID to build a Track.");
+ }
+ mMetadata = metadata;
+ mId = id;
+ mUri = uri;
+ }
+
+ /**
+ * Set optional extras for the track.
+ */
+ public MediaSession.Track.Builder setExtras(Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Create the {@link Track}.
+ */
+ public MediaSession.Track build() {
+ return new MediaSession.Track(mMetadata, mId, mUri, mExtras);
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mMetadata.writeToParcel(dest, flags);
+ dest.writeLong(mId);
+ mUri.writeToParcel(dest, flags);
+ dest.writeBundle(mExtras);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<MediaSession.Track> CREATOR
+ = new Creator<MediaSession.Track>() {
+
+ @Override
+ public MediaSession.Track createFromParcel(Parcel p) {
+ return new MediaSession.Track(p);
+ }
+
+ @Override
+ public MediaSession.Track[] newArray(int size) {
+ return new MediaSession.Track[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "MediaSession.Track {" +
+ "Metadata=" + mMetadata +
+ ", Id=" + mId +
+ ", Uri=" + mUri +
+ ", Extras=" + mExtras +
+ " }";
+ }
+ }
+
private class CallbackMessageHandler extends Handler {
private static final int MSG_MEDIA_BUTTON = 1;
private static final int MSG_COMMAND = 2;
@@ -913,7 +1199,7 @@
break;
case MSG_COMMAND:
Command cmd = (Command) msg.obj;
- mCallback.onControlCommand(cmd.command, cmd.extras, cmd.stub);
+ mCallback.onCommand(cmd.command, cmd.extras, cmd.stub);
break;
}
}
@@ -942,14 +1228,18 @@
private class TransportMessageHandler extends Handler {
private static final int MSG_PLAY = 1;
- private static final int MSG_PAUSE = 2;
- private static final int MSG_STOP = 3;
- private static final int MSG_NEXT = 4;
- private static final int MSG_PREVIOUS = 5;
- private static final int MSG_FAST_FORWARD = 6;
- private static final int MSG_REWIND = 7;
- private static final int MSG_SEEK_TO = 8;
- private static final int MSG_RATE = 9;
+ private static final int MSG_PLAY_URI = 2;
+ private static final int MSG_PLAY_SEARCH = 3;
+ private static final int MSG_SKIP_TO_TRACK = 4;
+ private static final int MSG_PAUSE = 5;
+ private static final int MSG_STOP = 6;
+ private static final int MSG_NEXT = 7;
+ private static final int MSG_PREVIOUS = 8;
+ private static final int MSG_FAST_FORWARD = 9;
+ private static final int MSG_REWIND = 10;
+ private static final int MSG_SEEK_TO = 11;
+ private static final int MSG_RATE = 12;
+ private static final int MSG_CUSTOM_ACTION = 13;
private TransportControlsCallback mCallback;
@@ -958,6 +1248,12 @@
mCallback = cb;
}
+ public void post(int what, Object obj, Bundle bundle) {
+ Message msg = obtainMessage(what, obj);
+ msg.setData(bundle);
+ msg.sendToTarget();
+ }
+
public void post(int what, Object obj) {
obtainMessage(what, obj).sendToTarget();
}
@@ -972,6 +1268,14 @@
case MSG_PLAY:
mCallback.onPlay();
break;
+ case MSG_PLAY_URI:
+ mCallback.onPlayUri((Uri) msg.obj, msg.getData());
+ break;
+ case MSG_PLAY_SEARCH:
+ mCallback.onPlayFromSearch((String) msg.obj, msg.getData());
+ break;
+ case MSG_SKIP_TO_TRACK:
+ mCallback.onSkipToTrack((Long) msg.obj);
case MSG_PAUSE:
mCallback.onPause();
break;
@@ -996,6 +1300,9 @@
case MSG_RATE:
mCallback.onSetRating((Rating) msg.obj);
break;
+ case MSG_CUSTOM_ACTION:
+ mCallback.onCustomAction((String) msg.obj, msg.getData());
+ break;
}
}
}
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index da1a6ed..a6963cf 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -21,6 +21,10 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.MediaMetadataEditor;
@@ -73,19 +77,23 @@
return sInstance;
}
- public static Bundle getOldMetadata(MediaMetadata metadata) {
+ public static Bundle getOldMetadata(MediaMetadata metadata, int artworkWidth,
+ int artworkHeight) {
+ boolean includeArtwork = artworkWidth != -1 && artworkHeight != -1;
Bundle oldMetadata = new Bundle();
if (metadata.containsKey(MediaMetadata.METADATA_KEY_ALBUM)) {
oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ALBUM),
metadata.getString(MediaMetadata.METADATA_KEY_ALBUM));
}
- if (metadata.containsKey(MediaMetadata.METADATA_KEY_ART)) {
+ if (includeArtwork && metadata.containsKey(MediaMetadata.METADATA_KEY_ART)) {
+ Bitmap art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK),
- metadata.getBitmap(MediaMetadata.METADATA_KEY_ART));
- } else if (metadata.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART)) {
+ scaleBitmapIfTooBig(art, artworkWidth, artworkHeight));
+ } else if (includeArtwork && metadata.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART)) {
// Fall back to album art if the track art wasn't available
+ Bitmap art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK),
- metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART));
+ scaleBitmapIfTooBig(art, artworkWidth, artworkHeight));
}
if (metadata.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ARTIST)) {
oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST),
@@ -322,6 +330,41 @@
}
}
+ /**
+ * Scale a bitmap to fit the smallest dimension by uniformly scaling the
+ * incoming bitmap. If the bitmap fits, then do nothing and return the
+ * original.
+ *
+ * @param bitmap
+ * @param maxWidth
+ * @param maxHeight
+ * @return
+ */
+ private static Bitmap scaleBitmapIfTooBig(Bitmap bitmap, int maxWidth, int maxHeight) {
+ if (bitmap != null) {
+ final int width = bitmap.getWidth();
+ final int height = bitmap.getHeight();
+ if (width > maxWidth || height > maxHeight) {
+ float scale = Math.min((float) maxWidth / width, (float) maxHeight / height);
+ int newWidth = Math.round(scale * width);
+ int newHeight = Math.round(scale * height);
+ Bitmap.Config newConfig = bitmap.getConfig();
+ if (newConfig == null) {
+ newConfig = Bitmap.Config.ARGB_8888;
+ }
+ Bitmap outBitmap = Bitmap.createBitmap(newWidth, newHeight, newConfig);
+ Canvas canvas = new Canvas(outBitmap);
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setFilterBitmap(true);
+ canvas.drawBitmap(bitmap, null,
+ new RectF(0, 0, outBitmap.getWidth(), outBitmap.getHeight()), paint);
+ bitmap = outBitmap;
+ }
+ }
+ return bitmap;
+ }
+
private SessionHolder getHolder(PendingIntent pi, boolean createIfMissing) {
SessionHolder holder = mSessions.get(pi);
if (holder == null && createIfMissing) {
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.java b/media/java/android/media/session/ParcelableVolumeInfo.java
index 166ccd3..e71b539 100644
--- a/media/java/android/media/session/ParcelableVolumeInfo.java
+++ b/media/java/android/media/session/ParcelableVolumeInfo.java
@@ -15,6 +15,7 @@
package android.media.session;
+import android.media.AudioAttributes;
import android.os.Parcel;
import android.os.Parcelable;
@@ -26,15 +27,16 @@
*/
public class ParcelableVolumeInfo implements Parcelable {
public int volumeType;
- public int audioStream;
+ public AudioAttributes audioAttrs;
public int controlType;
public int maxVolume;
public int currentVolume;
- public ParcelableVolumeInfo(int volumeType, int audioStream, int controlType, int maxVolume,
+ public ParcelableVolumeInfo(int volumeType, AudioAttributes audioAttrs, int controlType,
+ int maxVolume,
int currentVolume) {
this.volumeType = volumeType;
- this.audioStream = audioStream;
+ this.audioAttrs = audioAttrs;
this.controlType = controlType;
this.maxVolume = maxVolume;
this.currentVolume = currentVolume;
@@ -42,10 +44,10 @@
public ParcelableVolumeInfo(Parcel from) {
volumeType = from.readInt();
- audioStream = from.readInt();
controlType = from.readInt();
maxVolume = from.readInt();
currentVolume = from.readInt();
+ audioAttrs = AudioAttributes.CREATOR.createFromParcel(from);
}
@Override
@@ -56,10 +58,10 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(volumeType);
- dest.writeInt(audioStream);
dest.writeInt(controlType);
dest.writeInt(maxVolume);
dest.writeInt(currentVolume);
+ audioAttrs.writeToParcel(dest, flags);
}
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index f7e7176..65bd677 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -15,10 +15,17 @@
*/
package android.media.session;
+import android.annotation.DrawableRes;
import android.media.RemoteControlClient;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* Playback state for a {@link MediaSession}. This includes a state like
@@ -26,116 +33,140 @@
* and the current control capabilities.
*/
public final class PlaybackState implements Parcelable {
+ private static final String TAG = "PlaybackState";
+
/**
* Indicates this performer supports the stop command.
*
- * @see #setActions
+ * @see Builder#setActions(long)
*/
public static final long ACTION_STOP = 1 << 0;
/**
* Indicates this performer supports the pause command.
*
- * @see #setActions
+ * @see Builder#setActions(long)
*/
public static final long ACTION_PAUSE = 1 << 1;
/**
* Indicates this performer supports the play command.
*
- * @see #setActions
+ * @see Builder#setActions(long)
*/
public static final long ACTION_PLAY = 1 << 2;
/**
* Indicates this performer supports the rewind command.
*
- * @see #setActions
+ * @see Builder#setActions(long)
*/
public static final long ACTION_REWIND = 1 << 3;
/**
* Indicates this performer supports the previous command.
*
- * @see #setActions
+ * @see Builder#setActions(long)
*/
public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4;
/**
* Indicates this performer supports the next command.
*
- * @see #setActions
+ * @see Builder#setActions(long)
*/
public static final long ACTION_SKIP_TO_NEXT = 1 << 5;
/**
* Indicates this performer supports the fast forward command.
*
- * @see #setActions
+ * @see Builder#setActions(long)
*/
public static final long ACTION_FAST_FORWARD = 1 << 6;
/**
* Indicates this performer supports the set rating command.
*
- * @see #setActions
+ * @see Builder#setActions(long)
*/
public static final long ACTION_SET_RATING = 1 << 7;
/**
* Indicates this performer supports the seek to command.
*
- * @see #setActions
+ * @see Builder#setActions(long)
*/
public static final long ACTION_SEEK_TO = 1 << 8;
/**
* Indicates this performer supports the play/pause toggle command.
*
- * @see #setActions
+ * @see Builder#setActions(long)
*/
public static final long ACTION_PLAY_PAUSE = 1 << 9;
/**
+ * Indicates this performer supports the play from uri command.
+ *
+ * @see Builder#setActions(long)
+ */
+ public static final long ACTION_PLAY_URI = 1 << 10;
+
+ /**
+ * Indicates this performer supports the play from search command.
+ *
+ * @see Builder#setActions(long)
+ */
+ public static final long ACTION_PLAY_FROM_SEARCH = 1 << 11;
+
+ /**
+ * Indicates this performer supports the skip to track command.
+ *
+ * @see Builder#setActions(long)
+ */
+ public static final long ACTION_SKIP_TO_TRACK = 1 << 12;
+
+ /**
* This is the default playback state and indicates that no media has been
* added yet, or the performer has been reset and has no content to play.
*
- * @see #setState
+ * @see Builder#setState(int, long, float)
+ * @see Builder#setState(int, long, float, long)
*/
public final static int STATE_NONE = 0;
/**
* State indicating this item is currently stopped.
*
- * @see #setState
+ * @see Builder#setState
*/
public final static int STATE_STOPPED = 1;
/**
* State indicating this item is currently paused.
*
- * @see #setState
+ * @see Builder#setState
*/
public final static int STATE_PAUSED = 2;
/**
* State indicating this item is currently playing.
*
- * @see #setState
+ * @see Builder#setState
*/
public final static int STATE_PLAYING = 3;
/**
* State indicating this item is currently fast forwarding.
*
- * @see #setState
+ * @see Builder#setState
*/
public final static int STATE_FAST_FORWARDING = 4;
/**
* State indicating this item is currently rewinding.
*
- * @see #setState
+ * @see Builder#setState
*/
public final static int STATE_REWINDING = 5;
@@ -143,7 +174,7 @@
* State indicating this item is currently buffering and will begin playing
* when enough data has buffered.
*
- * @see #setState
+ * @see Builder#setState
*/
public final static int STATE_BUFFERING = 6;
@@ -151,7 +182,7 @@
* State indicating this item is currently in an error state. The error
* message should also be set when entering this state.
*
- * @see #setState
+ * @see Builder#setState
*/
public final static int STATE_ERROR = 7;
@@ -161,21 +192,21 @@
* state when the connection finishes or enter {@link #STATE_NONE}.
* If the connection failed {@link #STATE_ERROR} should be used.
*
- * @see #setState
+ * @see Builder#setState
*/
public final static int STATE_CONNECTING = 8;
/**
* State indicating the player is currently skipping to the previous item.
*
- * @see #setState
+ * @see Builder#setState
*/
public final static int STATE_SKIPPING_TO_PREVIOUS = 9;
/**
* State indicating the player is currently skipping to the next item.
*
- * @see #setState
+ * @see Builder#setState
*/
public final static int STATE_SKIPPING_TO_NEXT = 10;
@@ -189,17 +220,23 @@
private final long mBufferPosition;
private final float mSpeed;
private final long mActions;
+ private List<PlaybackState.CustomAction> mCustomActions;
private final CharSequence mErrorMessage;
private final long mUpdateTime;
+ private final long mActiveTrackId;
private PlaybackState(int state, long position, long updateTime, float speed,
- long bufferPosition, long actions, CharSequence error) {
+ long bufferPosition, long transportControls,
+ List<PlaybackState.CustomAction> customActions, long activeTrackId,
+ CharSequence error) {
mState = state;
mPosition = position;
mSpeed = speed;
mUpdateTime = updateTime;
mBufferPosition = bufferPosition;
- mActions = actions;
+ mActions = transportControls;
+ mCustomActions = new ArrayList<>(customActions);
+ mActiveTrackId = activeTrackId;
mErrorMessage = error;
}
@@ -210,6 +247,8 @@
mUpdateTime = in.readLong();
mBufferPosition = in.readLong();
mActions = in.readLong();
+ mCustomActions = in.createTypedArrayList(CustomAction.CREATOR);
+ mActiveTrackId = in.readLong();
mErrorMessage = in.readCharSequence();
}
@@ -223,6 +262,8 @@
bob.append(", speed=").append(mSpeed);
bob.append(", updated=").append(mUpdateTime);
bob.append(", actions=").append(mActions);
+ bob.append(", custom actions=").append(mCustomActions);
+ bob.append(", active track id=").append(mActiveTrackId);
bob.append(", error=").append(mErrorMessage);
bob.append("}");
return bob.toString();
@@ -241,6 +282,8 @@
dest.writeLong(mUpdateTime);
dest.writeLong(mBufferPosition);
dest.writeLong(mActions);
+ dest.writeTypedList(mCustomActions);
+ dest.writeLong(mActiveTrackId);
dest.writeCharSequence(mErrorMessage);
}
@@ -306,6 +349,13 @@
}
/**
+ * Get the list of custom actions.
+ */
+ public List<PlaybackState.CustomAction> getCustomActions() {
+ return mCustomActions;
+ }
+
+ /**
* Get a user readable error message. This should be set when the state is
* {@link PlaybackState#STATE_ERROR}.
*/
@@ -492,9 +542,173 @@
};
/**
+ * {@link PlaybackState.CustomAction CustomActions} can be used to extend the capabilities of
+ * the standard transport controls by exposing app specific actions to
+ * {@link MediaController MediaControllers}.
+ */
+ public static final class CustomAction implements Parcelable {
+ private final String mAction;
+ private final CharSequence mName;
+ private final int mIcon;
+ private final Bundle mExtras;
+
+ /**
+ * Use {@link PlaybackState.CustomAction.Builder#build()}.
+ */
+ private CustomAction(String action, CharSequence name, int icon, Bundle extras) {
+ mAction = action;
+ mName = name;
+ mIcon = icon;
+ mExtras = extras;
+ }
+
+ private CustomAction(Parcel in) {
+ mAction = in.readString();
+ mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mIcon = in.readInt();
+ mExtras = in.readBundle();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mAction);
+ TextUtils.writeToParcel(mName, dest, flags);
+ dest.writeInt(mIcon);
+ dest.writeBundle(mExtras);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<PlaybackState.CustomAction> CREATOR
+ = new Parcelable.Creator<PlaybackState.CustomAction>() {
+
+ @Override
+ public PlaybackState.CustomAction createFromParcel(Parcel p) {
+ return new PlaybackState.CustomAction(p);
+ }
+
+ @Override
+ public PlaybackState.CustomAction[] newArray(int size) {
+ return new PlaybackState.CustomAction[size];
+ }
+ };
+
+ /**
+ * Returns the action of the {@link CustomAction}.
+ *
+ * @return The action of the {@link CustomAction}.
+ */
+ public String getAction() {
+ return mAction;
+ }
+
+ /**
+ * Returns the display name of this action. e.g. "Favorite"
+ *
+ * @return The display name of this {@link CustomAction}.
+ */
+ public CharSequence getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the resource id of the icon in the {@link MediaSession MediaSession's} package.
+ *
+ * @return The resource id of the icon in the {@link MediaSession MediaSession's} package.
+ */
+ public int getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * Returns extras which provide additional application-specific information about the
+ * action, or null if none. These arguments are meant to be consumed by a
+ * {@link MediaController} if it knows how to handle them.
+ *
+ * @return Optional arguments for the {@link CustomAction}.
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public String toString() {
+ return "Action:" +
+ "mName='" + mName +
+ ", mIcon=" + mIcon +
+ ", mExtras=" + mExtras;
+ }
+
+ /**
+ * Builder for {@link CustomAction} objects.
+ */
+ public static final class Builder {
+ private final String mAction;
+ private final CharSequence mName;
+ private final int mIcon;
+ private Bundle mExtras;
+
+ /**
+ * Creates a {@link CustomAction} builder with the id, name, and icon set.
+ *
+ * @param action The action of the {@link CustomAction}.
+ * @param name The display name of the {@link CustomAction}. This name will be displayed
+ * along side the action if the UI supports it.
+ * @param icon The icon resource id of the {@link CustomAction}. This resource id
+ * must be in the same package as the {@link MediaSession}. It will be
+ * displayed with the custom action if the UI supports it.
+ */
+ public Builder(String action, CharSequence name, @DrawableRes int icon) {
+ if (TextUtils.isEmpty(action)) {
+ throw new IllegalArgumentException(
+ "You must specify an action to build a CustomAction.");
+ }
+ if (TextUtils.isEmpty(name)) {
+ throw new IllegalArgumentException(
+ "You must specify a name to build a CustomAction.");
+ }
+ if (icon == 0) {
+ throw new IllegalArgumentException(
+ "You must specify an icon resource id to build a CustomAction.");
+ }
+ mAction = action;
+ mName = name;
+ mIcon = icon;
+ }
+
+ /**
+ * Set optional extras for the {@link CustomAction}. These extras are meant to be
+ * consumed by a {@link MediaController} if it knows how to handle them.
+ * Keys should be fully qualified (e.g. "com.example.MY_ARG") to avoid collisions.
+ *
+ * @param extras Optional extras for the {@link CustomAction}.
+ * @return this.
+ */
+ public Builder setExtras(Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Build and return the {@link CustomAction} instance with the specified values.
+ *
+ * @return A new {@link CustomAction} instance.
+ */
+ public CustomAction build() {
+ return new CustomAction(mAction, mName, mIcon, mExtras);
+ }
+ }
+ }
+
+ /**
* Builder for {@link PlaybackState} objects.
*/
public static final class Builder {
+ private final List<PlaybackState.CustomAction> mCustomActions = new ArrayList<>();
+
private int mState;
private long mPosition;
private long mBufferPosition;
@@ -502,6 +716,7 @@
private long mActions;
private CharSequence mErrorMessage;
private long mUpdateTime;
+ private long mActiveTrackId = MediaSession.Track.UNKNOWN_ID;
/**
* Creates an initially empty state builder.
@@ -524,8 +739,12 @@
mBufferPosition = from.mBufferPosition;
mSpeed = from.mSpeed;
mActions = from.mActions;
+ if (from.mCustomActions != null) {
+ mCustomActions.addAll(from.mCustomActions);
+ }
mErrorMessage = from.mErrorMessage;
mUpdateTime = from.mUpdateTime;
+ mActiveTrackId = from.mActiveTrackId;
}
/**
@@ -626,12 +845,52 @@
}
/**
+ * Add a custom action to the playback state. Actions can be used to expose additional
+ * functionality to {@link MediaController MediaControllers} beyond what is offered by the
+ * standard transport controls.
+ * <p>
+ * e.g. start a radio station based on the current item or skip ahead by 30 seconds.
+ *
+ * @param action An identifier for this action. It will be sent back to the
+ * {@link MediaSession} through
+ * {@link
+ * MediaSession.TransportControlsCallback#onCustomAction(String, Bundle)}.
+ * @param name The display name for the action. If text is shown with the action or used
+ * for accessibility, this is what should be used.
+ * @param icon The resource action of the icon that should be displayed for the action. The
+ * resource should be in the package of the {@link MediaSession}.
+ * @return this
+ */
+ public Builder addCustomAction(String action, String name, int icon) {
+ return addCustomAction(new PlaybackState.CustomAction(action, name, icon, null));
+ }
+
+ /**
+ * Add a custom action to the playback state. Actions can be used to expose additional
+ * functionality to {@link MediaController MediaControllers} beyond what is offered by the
+ * standard transport controls.
+ * <p>
+ * An example of an action would be to start a radio station based on the current item
+ * or to skip ahead by 30 seconds.
+ *
+ * @param customAction The custom action to add to the {@link PlaybackState}.
+ * @return this
+ */
+ public Builder addCustomAction(PlaybackState.CustomAction customAction) {
+ if (customAction == null) {
+ throw new IllegalArgumentException(
+ "You may not add a null CustomAction to PlaybackState.");
+ }
+ mCustomActions.add(customAction);
+ return this;
+ }
+
+ /**
* Set the current buffer position in ms. This is the farthest playback
* point that can be reached from the current position using only
* buffered content.
*
- * @param bufferPosition The position in ms that playback is buffered
- * to.
+ * @param bufferPosition The position in ms that playback is buffered to.
* @return this
*/
public Builder setBufferPosition(long bufferPosition) {
@@ -640,6 +899,18 @@
}
/**
+ * Set the active track in the play queue by specifying its id.
+ * The default value is {@link MediaSession.Track#UNKNOWN_ID}
+ *
+ * @param id The id of the active track.
+ * @return this
+ */
+ public Builder setActiveTrack(long id) {
+ mActiveTrackId = id;
+ return this;
+ }
+
+ /**
* Set a user readable error message. This should be set when the state
* is {@link PlaybackState#STATE_ERROR}.
*
@@ -652,13 +923,13 @@
}
/**
- * Build and return the PlaybackState instance with these values.
+ * Build and return the {@link PlaybackState} instance with these values.
*
* @return A new state instance.
*/
public PlaybackState build() {
return new PlaybackState(mState, mPosition, mUpdateTime, mSpeed, mBufferPosition,
- mActions, mErrorMessage);
+ mActions, mCustomActions, mActiveTrackId, mErrorMessage);
}
}
}
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 7cf619d..559ea0c 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -24,8 +24,10 @@
import android.media.tv.ITvInputManagerCallback;
import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvInputInfo;
+import android.media.tv.TvStreamConfig;
import android.media.tv.TvTrackInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.view.Surface;
/**
@@ -42,6 +44,7 @@
void createSession(in ITvInputClient client, in String inputId, int seq, int userId);
void releaseSession(in IBinder sessionToken, int userId);
+ void setMainSession(in IBinder sessionToken, int userId);
void setSurface(in IBinder sessionToken, in Surface surface, int userId);
void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
int userId);
@@ -51,6 +54,9 @@
void selectTrack(in IBinder sessionToken, in TvTrackInfo track, int userId);
void unselectTrack(in IBinder sessionToken, in TvTrackInfo track, int userId);
+ void sendAppPrivateCommand(in IBinder sessionToken, in String action, in Bundle data,
+ int userId);
+
void createOverlayView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
int userId);
void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId);
@@ -60,12 +66,12 @@
// For TV input hardware binding
List<TvInputHardwareInfo> getHardwareList();
- /*
- * All TvInputServices which want to use hardware must call this method on
- * BOOT_COMPLETE.
- */
- void registerTvInputInfo(in TvInputInfo info, int deviceId);
ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
in TvInputInfo info, int userId);
void releaseTvInputHardware(int deviceId, in ITvInputHardware hardware, int userId);
+
+ // For TV input capturing
+ List<TvStreamConfig> getAvailableTvStreamConfigList(in String inputId, int userId);
+ boolean captureFrame(in String inputId, in Surface surface, in TvStreamConfig config,
+ int userId);
}
diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
index 158223a..651669b 100644
--- a/media/java/android/media/tv/ITvInputService.aidl
+++ b/media/java/android/media/tv/ITvInputService.aidl
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.media.tv.ITvInputServiceCallback;
import android.media.tv.ITvInputSessionCallback;
import android.media.tv.TvInputHardwareInfo;
@@ -32,6 +33,8 @@
in String inputId);
// For hardware TvInputService
- void notifyHardwareAdded(in TvInputHardwareInfo info);
- void notifyHardwareRemoved(int deviceId);
+ void notifyHardwareAdded(in TvInputHardwareInfo hardwareInfo);
+ void notifyHardwareRemoved(in TvInputHardwareInfo hardwareInfo);
+ void notifyHdmiCecDeviceAdded(in HdmiCecDeviceInfo cecDeviceInfo);
+ void notifyHdmiCecDeviceRemoved(in HdmiCecDeviceInfo cecDeviceInfo);
}
diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl
index 287da71..26a0d20 100644
--- a/media/java/android/media/tv/ITvInputServiceCallback.aidl
+++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl
@@ -24,6 +24,8 @@
* @hide
*/
oneway interface ITvInputServiceCallback {
- void addTvInput(in TvInputInfo inputInfo);
+ void addHardwareTvInput(in int deviceID, in TvInputInfo inputInfo);
+ void addHdmiCecTvInput(in int logicalAddress, in TvInputInfo inputInfo);
void removeTvInput(in String inputId);
+ void setWrappedInputId(in String inputId, in String wrappedInputId);
}
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index efcb1c6..df14ad6 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -19,6 +19,7 @@
import android.graphics.Rect;
import android.media.tv.TvTrackInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.view.Surface;
/**
@@ -28,6 +29,7 @@
oneway interface ITvInputSession {
void release();
+ void setMainSession(boolean isMainSession);
void setSurface(in Surface surface);
void dispatchSurfaceChanged(int format, int width, int height);
// TODO: Remove this once it becomes irrelevant for applications to handle audio focus. The plan
@@ -38,6 +40,8 @@
void selectTrack(in TvTrackInfo track);
void unselectTrack(in TvTrackInfo track);
+ void appPrivateCommand(in String action, in Bundle data);
+
void createOverlayView(in IBinder windowToken, in Rect frame);
void relayoutOverlayView(in Rect frame);
void removeOverlayView();
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 9683040..35a68a4 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.graphics.Rect;
import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -41,17 +42,19 @@
private static final String TAG = "TvInputSessionWrapper";
private static final int DO_RELEASE = 1;
- private static final int DO_SET_SURFACE = 2;
- private static final int DO_DISPATCH_SURFACE_CHANGED = 3;
- private static final int DO_SET_STREAM_VOLUME = 4;
- private static final int DO_TUNE = 5;
- private static final int DO_SET_CAPTION_ENABLED = 6;
- private static final int DO_SELECT_TRACK = 7;
- private static final int DO_UNSELECT_TRACK = 8;
- private static final int DO_CREATE_OVERLAY_VIEW = 9;
- private static final int DO_RELAYOUT_OVERLAY_VIEW = 10;
- private static final int DO_REMOVE_OVERLAY_VIEW = 11;
- private static final int DO_REQUEST_UNBLOCK_CONTENT = 12;
+ private static final int DO_SET_MAIN_SESSION = 2;
+ private static final int DO_SET_SURFACE = 3;
+ private static final int DO_DISPATCH_SURFACE_CHANGED = 4;
+ private static final int DO_SET_STREAM_VOLUME = 5;
+ private static final int DO_TUNE = 6;
+ private static final int DO_SET_CAPTION_ENABLED = 7;
+ private static final int DO_SELECT_TRACK = 8;
+ private static final int DO_UNSELECT_TRACK = 9;
+ private static final int DO_APP_PRIVATE_COMMAND = 10;
+ private static final int DO_CREATE_OVERLAY_VIEW = 11;
+ private static final int DO_RELAYOUT_OVERLAY_VIEW = 12;
+ private static final int DO_REMOVE_OVERLAY_VIEW = 13;
+ private static final int DO_REQUEST_UNBLOCK_CONTENT = 14;
private final HandlerCaller mCaller;
@@ -89,6 +92,10 @@
}
return;
}
+ case DO_SET_MAIN_SESSION: {
+ mTvInputSessionImpl.setMainSession((Boolean) msg.obj);
+ return;
+ }
case DO_SET_SURFACE: {
mTvInputSessionImpl.setSurface((Surface) msg.obj);
return;
@@ -119,6 +126,12 @@
mTvInputSessionImpl.unselectTrack((TvTrackInfo) msg.obj);
return;
}
+ case DO_APP_PRIVATE_COMMAND: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mTvInputSessionImpl.appPrivateCommand((String) args.arg1, (Bundle) args.arg2);
+ args.recycle();
+ return;
+ }
case DO_CREATE_OVERLAY_VIEW: {
SomeArgs args = (SomeArgs) msg.obj;
mTvInputSessionImpl.createOverlayView((IBinder) args.arg1, (Rect) args.arg2);
@@ -150,6 +163,11 @@
}
@Override
+ public void setMainSession(boolean isMain) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_MAIN_SESSION, isMain));
+ }
+
+ @Override
public void setSurface(Surface surface) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_SURFACE, surface));
}
@@ -186,6 +204,12 @@
}
@Override
+ public void appPrivateCommand(String action, Bundle data) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action,
+ data));
+ }
+
+ @Override
public void createOverlayView(IBinder windowToken, Rect frame) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_OVERLAY_VIEW, windowToken,
frame));
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 78e0404..d2071ef 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -120,6 +121,15 @@
}
/**
+ * Returns true, if {@code channelUri} is a channel URI for a passthrough TV input.
+ * @hide
+ */
+ @SystemApi
+ public static final boolean isChannelUriForPassthroughTvInput(Uri channelUri) {
+ return channelUri.toString().endsWith(PATH_PASSTHROUGH);
+ }
+
+ /**
* Builds a URI that points to a channel logo. See {@link Channels.Logo}.
*
* @param channelId The ID of the channel whose logo is pointed to.
@@ -340,85 +350,85 @@
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
/** A generic channel type. */
- public static final int TYPE_OTHER = 0x0;
+ public static final String TYPE_OTHER = "TYPE_OTHER";
/** The channel type for NTSC. */
- public static final int TYPE_NTSC = 0x1;
+ public static final String TYPE_NTSC = "TYPE_NTSC";
/** The channel type for PAL. */
- public static final int TYPE_PAL = 0x2;
+ public static final String TYPE_PAL = "TYPE_PAL";
/** The channel type for SECAM. */
- public static final int TYPE_SECAM = 0x3;
+ public static final String TYPE_SECAM = "TYPE_SECAM";
/** The channel type for DVB-T (terrestrial). */
- public static final int TYPE_DVB_T = 0x00010000;
+ public static final String TYPE_DVB_T = "TYPE_DVB_T";
/** The channel type for DVB-T2 (terrestrial). */
- public static final int TYPE_DVB_T2 = 0x00010001;
+ public static final String TYPE_DVB_T2 = "TYPE_DVB_T2";
/** The channel type for DVB-S (satellite). */
- public static final int TYPE_DVB_S = 0x00010100;
+ public static final String TYPE_DVB_S = "TYPE_DVB_S";
/** The channel type for DVB-S2 (satellite). */
- public static final int TYPE_DVB_S2 = 0x00010101;
+ public static final String TYPE_DVB_S2 = "TYPE_DVB_S2";
/** The channel type for DVB-C (cable). */
- public static final int TYPE_DVB_C = 0x00010200;
+ public static final String TYPE_DVB_C = "TYPE_DVB_C";
/** The channel type for DVB-C2 (cable). */
- public static final int TYPE_DVB_C2 = 0x00010201;
+ public static final String TYPE_DVB_C2 = "TYPE_DVB_C2";
/** The channel type for DVB-H (handheld). */
- public static final int TYPE_DVB_H = 0x00010300;
+ public static final String TYPE_DVB_H = "TYPE_DVB_H";
/** The channel type for DVB-SH (satellite). */
- public static final int TYPE_DVB_SH = 0x00010400;
+ public static final String TYPE_DVB_SH = "TYPE_DVB_SH";
/** The channel type for ATSC (terrestrial). */
- public static final int TYPE_ATSC_T = 0x00020000;
+ public static final String TYPE_ATSC_T = "TYPE_ATSC_T";
/** The channel type for ATSC (cable). */
- public static final int TYPE_ATSC_C = 0x00020200;
+ public static final String TYPE_ATSC_C = "TYPE_ATSC_C";
/** The channel type for ATSC-M/H (mobile/handheld). */
- public static final int TYPE_ATSC_M_H = 0x00020300;
+ public static final String TYPE_ATSC_M_H = "TYPE_ATSC_M_H";
/** The channel type for ISDB-T (terrestrial). */
- public static final int TYPE_ISDB_T = 0x00030000;
+ public static final String TYPE_ISDB_T = "TYPE_ISDB_T";
/** The channel type for ISDB-Tb (Brazil). */
- public static final int TYPE_ISDB_TB = 0x00030100;
+ public static final String TYPE_ISDB_TB = "TYPE_ISDB_TB";
/** The channel type for ISDB-S (satellite). */
- public static final int TYPE_ISDB_S = 0x00030200;
+ public static final String TYPE_ISDB_S = "TYPE_ISDB_S";
/** The channel type for ISDB-C (cable). */
- public static final int TYPE_ISDB_C = 0x00030300;
+ public static final String TYPE_ISDB_C = "TYPE_ISDB_C";
/** The channel type for 1seg (handheld). */
- public static final int TYPE_1SEG = 0x00030400;
+ public static final String TYPE_1SEG = "TYPE_1SEG";
/** The channel type for DTMB (terrestrial). */
- public static final int TYPE_DTMB = 0x00040000;
+ public static final String TYPE_DTMB = "TYPE_DTMB";
/** The channel type for CMMB (handheld). */
- public static final int TYPE_CMMB = 0x00040100;
+ public static final String TYPE_CMMB = "TYPE_CMMB";
/** The channel type for T-DMB (terrestrial). */
- public static final int TYPE_T_DMB = 0x00050000;
+ public static final String TYPE_T_DMB = "TYPE_T_DMB";
/** The channel type for S-DMB (satellite). */
- public static final int TYPE_S_DMB = 0x00050100;
+ public static final String TYPE_S_DMB = "TYPE_S_DMB";
/** A generic service type. */
- public static final int SERVICE_TYPE_OTHER = 0x0;
+ public static final String SERVICE_TYPE_OTHER = "SERVICE_TYPE_OTHER";
/** The service type for regular TV channels that have both audio and video. */
- public static final int SERVICE_TYPE_AUDIO_VIDEO = 0x1;
+ public static final String SERVICE_TYPE_AUDIO_VIDEO = "SERVICE_TYPE_AUDIO_VIDEO";
/** The service type for radio channels that have audio only. */
- public static final int SERVICE_TYPE_AUDIO = 0x2;
+ public static final String SERVICE_TYPE_AUDIO = "SERVICE_TYPE_AUDIO";
/** The video format for 240p. */
public static final String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P";
@@ -521,7 +531,7 @@
* </p><p>
* This is a required field.
* </p><p>
- * Type: INTEGER
+ * Type: TEXT
* </p>
*/
public static final String COLUMN_TYPE = "type";
@@ -537,7 +547,7 @@
* </p><p>
* This is a required field.
* </p><p>
- * Type: INTEGER
+ * Type: TEXT
* </p>
*/
public static final String COLUMN_SERVICE_TYPE = "service_type";
@@ -809,12 +819,46 @@
/**
* The title of this TV program.
* <p>
+ * If this program is an episodic TV show, it is recommended that the title is the series
+ * title and its related fields ({@link #COLUMN_SEASON_NUMBER},
+ * {@link #COLUMN_EPISODE_NUMBER}, and {@link #COLUMN_EPISODE_TITLE}) are filled in.
+ * </p><p>
* Type: TEXT
* </p>
**/
public static final String COLUMN_TITLE = "title";
/**
+ * The season number of this TV program for episodic TV shows.
+ * <p>
+ * Can be empty.
+ * </p><p>
+ * Type: INTEGER
+ * </p>
+ **/
+ public static final String COLUMN_SEASON_NUMBER = "season_number";
+
+ /**
+ * The episode number of this TV program for episodic TV shows.
+ * <p>
+ * Can be empty.
+ * </p><p>
+ * Type: INTEGER
+ * </p>
+ **/
+ public static final String COLUMN_EPISODE_NUMBER = "episode_number";
+
+ /**
+ * The episode title of this TV program for episodic TV shows.
+ * <p>
+ * Can be empty.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ **/
+ public static final String COLUMN_EPISODE_TITLE = "episode_title";
+
+ /**
* The start time of this TV program, in milliseconds since the epoch.
* <p>
* Type: INTEGER (long)
@@ -1065,6 +1109,7 @@
* @return {@code true} if the genre is canonical, otherwise {@code false}.
* @hide
*/
+ @SystemApi
public static boolean isCanonical(String genre) {
return CANONICAL_GENRES.contains(genre);
}
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 77cfc32..52c0534 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -28,17 +29,20 @@
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.SparseIntArray;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.InputStream;
/**
* This class is used to specify meta information of a TV input.
@@ -47,60 +51,55 @@
private static final boolean DEBUG = false;
private static final String TAG = "TvInputInfo";
+ // Should be in sync with frameworks/base/core/res/res/values/attrs.xml
/**
- * TV input type: the TV input service is not handling input from hardware. For example,
- * services showing streaming from the internet falls into this type.
+ * TV input type: the TV input service is a tuner which provides channels.
*/
- public static final int TYPE_VIRTUAL = 0;
-
- // Should be in sync with TvInputHardwareInfo.
-
+ public static final int TYPE_TUNER = 0;
/**
* TV input type: a generic hardware TV input type.
*/
- public static final int TYPE_OTHER_HARDWARE = TvInputHardwareInfo.TV_INPUT_TYPE_OTHER_HARDWARE;
- /**
- * TV input type: the TV input service is a tuner. (e.g. terrestrial tuner)
- */
- public static final int TYPE_TUNER = TvInputHardwareInfo.TV_INPUT_TYPE_TUNER;
+ public static final int TYPE_OTHER = 1000;
/**
* TV input type: the TV input service represents a composite port.
*/
- public static final int TYPE_COMPOSITE = TvInputHardwareInfo.TV_INPUT_TYPE_COMPOSITE;
+ public static final int TYPE_COMPOSITE = 1001;
/**
* TV input type: the TV input service represents a SVIDEO port.
*/
- public static final int TYPE_SVIDEO = TvInputHardwareInfo.TV_INPUT_TYPE_SVIDEO;
+ public static final int TYPE_SVIDEO = 1002;
/**
* TV input type: the TV input service represents a SCART port.
*/
- public static final int TYPE_SCART = TvInputHardwareInfo.TV_INPUT_TYPE_SCART;
+ public static final int TYPE_SCART = 1003;
/**
* TV input type: the TV input service represents a component port.
*/
- public static final int TYPE_COMPONENT = TvInputHardwareInfo.TV_INPUT_TYPE_COMPONENT;
+ public static final int TYPE_COMPONENT = 1004;
/**
* TV input type: the TV input service represents a VGA port.
*/
- public static final int TYPE_VGA = TvInputHardwareInfo.TV_INPUT_TYPE_VGA;
+ public static final int TYPE_VGA = 1005;
/**
* TV input type: the TV input service represents a DVI port.
*/
- public static final int TYPE_DVI = TvInputHardwareInfo.TV_INPUT_TYPE_DVI;
+ public static final int TYPE_DVI = 1006;
/**
* TV input type: the TV input service is HDMI. (e.g. HDMI 1)
*/
- public static final int TYPE_HDMI = TvInputHardwareInfo.TV_INPUT_TYPE_HDMI;
+ public static final int TYPE_HDMI = 1007;
/**
* TV input type: the TV input service represents a display port.
*/
- public static final int TYPE_DISPLAY_PORT = TvInputHardwareInfo.TV_INPUT_TYPE_DISPLAY_PORT;
+ public static final int TYPE_DISPLAY_PORT = 1008;
/**
* The ID of the TV input to provide to the setup activity and settings activity.
*/
public static final String EXTRA_INPUT_ID = "inputId";
+ private static SparseIntArray sHardwareTypeToTvInputType = new SparseIntArray();
+
private static final String XML_START_TAG_NAME = "tv-input";
private final ResolveInfo mService;
@@ -110,7 +109,24 @@
// Attributes from XML meta data.
private String mSetupActivity;
private String mSettingsActivity;
- private int mType = TYPE_VIRTUAL;
+ private int mType = TYPE_TUNER;
+ private String mLabel;
+ private Uri mIconUri;
+
+ static {
+ sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_OTHER_HARDWARE,
+ TYPE_OTHER);
+ sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_TUNER, TYPE_TUNER);
+ sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_COMPOSITE, TYPE_COMPOSITE);
+ sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_SVIDEO, TYPE_SVIDEO);
+ sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_SCART, TYPE_SCART);
+ sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_COMPONENT, TYPE_COMPONENT);
+ sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_VGA, TYPE_VGA);
+ sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_DVI, TYPE_DVI);
+ sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_HDMI, TYPE_HDMI);
+ sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_DISPLAY_PORT,
+ TYPE_DISPLAY_PORT);
+ }
/**
* Create a new instance of the TvInputInfo class,
@@ -122,7 +138,8 @@
public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service)
throws XmlPullParserException, IOException {
return createTvInputInfo(context, service, generateInputIdForComponentName(
- new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name)));
+ new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name)),
+ null, TYPE_TUNER, null, null);
}
/**
@@ -131,13 +148,21 @@
*
* @param service The ResolveInfo returned from the package manager about this TV input service.
* @param cecInfo The HdmiCecDeviceInfo for a HDMI CEC logical device.
+ * @param parentId The ID of this TV input's parent input. {@code null} if none exists.
+ * @param iconUri The {@link android.net.Uri} to load the icon image.
+ * {@see android.content.ContentResolver#openInputStream}. If it is null, the application
+ * icon of {@code service} will be loaded.
+ * @param label The label of this TvInputInfo. If it is null or empty, {@code service} label
+ * will be loaded.
* @hide
*/
+ @SystemApi
public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
- HdmiCecDeviceInfo cecInfo) throws XmlPullParserException, IOException {
+ HdmiCecDeviceInfo cecInfo, String parentId, String label, Uri iconUri)
+ throws XmlPullParserException, IOException {
return createTvInputInfo(context, service, generateInputIdForHdmiCec(
new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
- cecInfo));
+ cecInfo), parentId, TYPE_HDMI, label, iconUri);
}
/**
@@ -146,17 +171,26 @@
*
* @param service The ResolveInfo returned from the package manager about this TV input service.
* @param hardwareInfo The TvInputHardwareInfo for a TV input hardware device.
+ * @param iconUri The {@link android.net.Uri} to load the icon image.
+ * {@see android.content.ContentResolver#openInputStream}. If it is null, the application
+ * icon of {@code service} will be loaded.
+ * @param label The label of this TvInputInfo. If it is null or empty, {@code service} label
+ * will be loaded.
* @hide
*/
+ @SystemApi
public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
- TvInputHardwareInfo hardwareInfo) throws XmlPullParserException, IOException {
+ TvInputHardwareInfo hardwareInfo, String label, Uri iconUri)
+ throws XmlPullParserException, IOException {
+ int inputType = sHardwareTypeToTvInputType.get(hardwareInfo.getType(), TYPE_TUNER);
return createTvInputInfo(context, service, generateInputIdForHardware(
new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
- hardwareInfo));
+ hardwareInfo), null, inputType, label, iconUri);
}
private static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
- String id) throws XmlPullParserException, IOException {
+ String id, String parentId, int inputType, String label, Uri iconUri)
+ throws XmlPullParserException, IOException {
ServiceInfo si = service.serviceInfo;
PackageManager pm = context.getPackageManager();
XmlResourceParser parser = null;
@@ -181,7 +215,7 @@
"Meta-data does not start with tv-input-service tag in " + si.name);
}
- TvInputInfo input = new TvInputInfo(context, service, id, null);
+ TvInputInfo input = new TvInputInfo(service, id, parentId, inputType);
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.TvInputService);
input.mSetupActivity = sa.getString(
@@ -195,16 +229,10 @@
Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for "
+ si.name);
}
- if (pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE, si.packageName)
- == PackageManager.PERMISSION_GRANTED) {
- input.mType = sa.getInt(
- com.android.internal.R.styleable.TvInputService_tvInputType, TYPE_VIRTUAL);
- if (DEBUG) {
- Log.d(TAG, "Type loaded. [" + input.mType + "] for " + si.name);
- }
- }
sa.recycle();
+ input.mLabel = label;
+ input.mIconUri = iconUri;
return input;
} catch (NameNotFoundException e) {
throw new XmlPullParserException("Unable to create context for: " + si.packageName);
@@ -221,12 +249,13 @@
* @param service The ResolveInfo returned from the package manager about this TV input service.
* @param id ID of this TV input. Should be generated via generateInputId*().
* @param parentId ID of this TV input's parent input. {@code null} if none exists.
+ * @param type The type of this TV input service.
*/
- private TvInputInfo(Context context, ResolveInfo service, String id, String parentId) {
+ private TvInputInfo(ResolveInfo service, String id, String parentId, int type) {
mService = service;
- ServiceInfo si = service.serviceInfo;
mId = id;
mParentId = parentId;
+ mType = type;
}
/**
@@ -314,12 +343,7 @@
* @see TvContract#buildChannelUriForPassthroughTvInput(String)
*/
public boolean isPassthroughInputType() {
- if (mType == TYPE_HDMI || mType == TYPE_DISPLAY_PORT || mType == TYPE_SCART
- || mType == TYPE_DVI || mType == TYPE_VGA || mType == TYPE_COMPONENT
- || mType == TYPE_COMPOSITE || mType == TYPE_SVIDEO) {
- return true;
- }
- return false;
+ return mType != TYPE_TUNER;
}
/**
@@ -330,7 +354,11 @@
* a label, its name is returned.
*/
public CharSequence loadLabel(Context context) {
- return mService.loadLabel(context.getPackageManager());
+ if (TextUtils.isEmpty(mLabel)) {
+ return mService.loadLabel(context.getPackageManager());
+ } else {
+ return mLabel;
+ }
}
/**
@@ -342,7 +370,19 @@
* returned.
*/
public Drawable loadIcon(Context context) {
- return mService.serviceInfo.loadIcon(context.getPackageManager());
+ if (mIconUri == null) {
+ return loadDefaultIcon(context);
+ }
+ try (InputStream is = context.getContentResolver().openInputStream(mIconUri)) {
+ Drawable drawable = Drawable.createFromStream(is, null);
+ if (drawable == null) {
+ return loadDefaultIcon(context);
+ }
+ return drawable;
+ } catch (IOException e) {
+ Log.w(TAG, "Loading the default icon due to a failure on loading " + mIconUri, e);
+ return loadDefaultIcon(context);
+ }
}
@Override
@@ -390,6 +430,12 @@
dest.writeString(mSetupActivity);
dest.writeString(mSettingsActivity);
dest.writeInt(mType);
+ dest.writeString(mIconUri == null ? null : mIconUri.toString());
+ dest.writeString(mLabel);
+ }
+
+ private Drawable loadDefaultIcon(Context context) {
+ return mService.serviceInfo.loadIcon(context.getPackageManager());
}
/**
@@ -429,7 +475,6 @@
/**
* Used to make this class parcelable.
- *
* @hide
*/
public static final Parcelable.Creator<TvInputInfo> CREATOR =
@@ -452,5 +497,10 @@
mSetupActivity = in.readString();
mSettingsActivity = in.readString();
mType = in.readInt();
+ String mIconUriString = in.readString();
+ if (mIconUriString != null) {
+ mIconUri = Uri.parse(mIconUriString);
+ }
+ mLabel = in.readString();
}
}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 200b3e9..4bb0c53 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.SystemApi;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
@@ -125,6 +126,7 @@
* Interface used to receive the created session.
* @hide
*/
+ @SystemApi
public abstract static class SessionCallback {
/**
* This is called after {@link TvInputManager#createSession} has been processed.
@@ -213,6 +215,7 @@
* @param eventArgs Optional arguments of the event.
* @hide
*/
+ @SystemApi
public void onSessionEvent(Session session, String eventType, Bundle eventArgs) {
}
}
@@ -655,6 +658,7 @@
* @throws IllegalArgumentException if any of the arguments is {@code null}.
* @hide
*/
+ @SystemApi
public void createSession(String inputId, final SessionCallback callback,
Handler handler) {
if (inputId == null) {
@@ -679,9 +683,45 @@
}
/**
+ * Returns the TvStreamConfig list of the given TV input.
+ *
+ * @param inputId the id of the TV input.
+ * @return List of {@link TvStreamConfig} which is available for capturing
+ * of the given TV input.
+ * @hide
+ */
+ @SystemApi
+ public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId) {
+ try {
+ return mService.getAvailableTvStreamConfigList(inputId, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Take a snapshot of the given TV input into the provided Surface.
+ *
+ * @param inputId the id of the TV input.
+ * @param surface the {@link Surface} to which the snapshot is captured.
+ * @param config the {@link TvStreamConfig} which is used for capturing.
+ * @return true when the {@link Surface} is ready to be captured.
+ * @hide
+ */
+ @SystemApi
+ public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config) {
+ try {
+ return mService.captureFrame(inputId, surface, config, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* The Session provides the per-session functionality of TV inputs.
* @hide
*/
+ @SystemApi
public static final class Session {
static final int DISPATCH_IN_PROGRESS = -1;
static final int DISPATCH_NOT_HANDLED = 0;
@@ -706,7 +746,6 @@
private InputChannel mChannel;
private List<TvTrackInfo> mTracks;
- /** @hide */
private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
mToken = token;
@@ -735,10 +774,25 @@
}
/**
+ * Set this as main session. See {@link TvView#setMainTvView} for about meaning of "main".
+ * @hide
+ */
+ public void setMainSession() {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.setMainSession(mToken, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* Sets the {@link android.view.Surface} for this session.
*
* @param surface A {@link android.view.Surface} used to render video.
- * @hide
*/
public void setSurface(Surface surface) {
if (mToken == null) {
@@ -762,6 +816,7 @@
* @param height The new height of the {@link Surface}.
* @hide
*/
+ @SystemApi
public void dispatchSurfaceChanged(int format, int width, int height) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
@@ -894,6 +949,29 @@
}
/**
+ * Call {@link TvInputService.Session#appPrivateCommand(String, Bundle)
+ * TvInputService.Session.appPrivateCommand()} on the current TvView.
+ *
+ * @param action Name of the command to be performed. This <em>must</em> be a scoped name,
+ * i.e. prefixed with a package name you own, so that different developers will
+ * not create conflicting commands.
+ * @param data Any data to include with the command.
+ * @hide
+ */
+ @SystemApi
+ public void sendAppPrivateCommand(String action, Bundle data) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.sendAppPrivateCommand(mToken, action, data, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* Creates an overlay view. Once the overlay view is created, {@link #relayoutOverlayView}
* should be called whenever the layout of its containing view is changed.
* {@link #removeOverlayView()} should be called to remove the overlay view.
diff --git a/media/java/android/media/tv/TvInputPassthroughWrapperService.java b/media/java/android/media/tv/TvInputPassthroughWrapperService.java
index 562dc2f..e99044d 100644
--- a/media/java/android/media/tv/TvInputPassthroughWrapperService.java
+++ b/media/java/android/media/tv/TvInputPassthroughWrapperService.java
@@ -75,6 +75,7 @@
if (!session.connect(passthroughInputId)) {
throw new IllegalStateException("WrapperSession cannot be reused.");
}
+ notifyWrappedInputId(inputId, passthroughInputId);
return session;
}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index f923827..d5111f4 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -121,21 +122,32 @@
@Override
public void notifyHardwareAdded(TvInputHardwareInfo hardwareInfo) {
- mHandler.obtainMessage(ServiceHandler.DO_ADD_TV_INPUT_FROM_HARDWARE,
+ mHandler.obtainMessage(ServiceHandler.DO_ADD_HARDWARE_TV_INPUT,
hardwareInfo).sendToTarget();
}
@Override
- public void notifyHardwareRemoved(int deviceId) {
- mHandler.obtainMessage(ServiceHandler.DO_REMOVE_TV_INPUT_FROM_HARDWARE,
- deviceId, 0).sendToTarget();
+ public void notifyHardwareRemoved(TvInputHardwareInfo hardwareInfo) {
+ mHandler.obtainMessage(ServiceHandler.DO_REMOVE_HARDWARE_TV_INPUT,
+ hardwareInfo).sendToTarget();
+ }
+
+ @Override
+ public void notifyHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDeviceInfo) {
+ mHandler.obtainMessage(ServiceHandler.DO_ADD_HDMI_CEC_TV_INPUT,
+ cecDeviceInfo).sendToTarget();
+ }
+
+ @Override
+ public void notifyHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDeviceInfo) {
+ mHandler.obtainMessage(ServiceHandler.DO_REMOVE_HDMI_CEC_TV_INPUT,
+ cecDeviceInfo).sendToTarget();
}
};
}
/**
* Get the number of callbacks that are registered.
- *
* @hide
*/
@VisibleForTesting
@@ -153,11 +165,11 @@
public abstract Session onCreateSession(String inputId);
/**
- * Returns a new TvInputInfo object if this service is responsible for {@code hardwareInfo};
- * otherwise, return {@code null}. Override to modify default behavior of ignoring all input.
+ * Returns a new {@link TvInputInfo} object if this service is responsible for
+ * {@code hardwareInfo}; otherwise, return {@code null}. Override to modify default behavior of
+ * ignoring all hardware input.
*
- * @param hardwareInfo TvInputHardwareInfo object just added.
- *
+ * @param hardwareInfo {@link TvInputHardwareInfo} object just added.
* @hide
*/
@SystemApi
@@ -167,16 +179,59 @@
/**
* Returns the input ID for {@code deviceId} if it is handled by this service;
- * otherwise, return {@code null}. Override to modify default behavior of ignoring all input.
+ * otherwise, return {@code null}. Override to modify default behavior of ignoring all hardware
+ * input.
*
+ * @param hardwareInfo {@link TvInputHardwareInfo} object just removed.
* @hide
*/
@SystemApi
- public String onHardwareRemoved(int deviceId) {
+ public String onHardwareRemoved(TvInputHardwareInfo hardwareInfo) {
return null;
}
/**
+ * Returns a new {@link TvInputInfo} object if this service is responsible for
+ * {@code cecDeviceInfo}; otherwise, return {@code null}. Override to modify default behavior
+ * of ignoring all HDMI CEC logical input device.
+ *
+ * @param cecDeviceInfo {@link HdmiCecDeviceInfo} object just added.
+ * @hide
+ */
+ @SystemApi
+ public TvInputInfo onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDeviceInfo) {
+ return null;
+ }
+
+ /**
+ * Returns the input ID for {@code logicalAddress} if it is handled by this service;
+ * otherwise, return {@code null}. Override to modify default behavior of ignoring all HDMI CEC
+ * logical input device.
+ *
+ * @param cecDeviceInfo {@link HdmiCecDeviceInfo} object just removed.
+ * @hide
+ */
+ @SystemApi
+ public String onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDeviceInfo) {
+ return null;
+ }
+
+ /**
+ * Notify wrapped TV input ID of current input to TV input framework manager
+ *
+ * @param inputId The TV input ID of {@link TvInputPassthroughWrapperService}
+ * @param wrappedInputId The ID of the wrapped TV input such as external pass-though TV input
+ * @hide
+ */
+ public final void notifyWrappedInputId(String inputId, String wrappedInputId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = inputId;
+ args.arg2 = wrappedInputId;
+ mHandler.obtainMessage(TvInputService.ServiceHandler.DO_SET_WRAPPED_TV_INPUT_ID,
+ args).sendToTarget();
+ }
+
+ /**
* Base class for derived classes to implement to provide a TV input session.
*/
public abstract class Session implements KeyEvent.Callback {
@@ -419,6 +474,32 @@
public abstract void onRelease();
/**
+ * Set the current session as the "main" session. See {@link TvView#setMainTvView} for the
+ * meaning of "main".
+ * <p>
+ * This is primarily for HDMI-CEC active source management. TV input service that manages
+ * HDMI-CEC logical device should make sure not only to select the corresponding HDMI
+ * logical device as source device on {@code onSetMainSession(true)}, but also to select
+ * internal device on {@code onSetMainSession(false)}. Also, if surface is set to non-main
+ * session, it needs to select internal device after temporarily selecting corresponding
+ * HDMI logical device for set up.
+ * </p><p>
+ * It is guaranteed that {@code onSetMainSession(true)} for new session is called first,
+ * and {@code onSetMainSession(false)} for old session is called afterwards. This allows
+ * {@code onSetMainSession(false)} to be no-op when TV input service knows that the next
+ * main session corresponds to another HDMI logical device. Practically, this implies that
+ * one TV input service should handle all HDMI port and HDMI-CEC logical devices for smooth
+ * active source transition.
+ * </p>
+ *
+ * @param isMainSession If true, session is main.
+ * @hide
+ */
+ @SystemApi
+ public void onSetMainSession(boolean isMainSession) {
+ }
+
+ /**
* Sets the {@link Surface} for the current input session on which the TV input renders
* video.
*
@@ -519,6 +600,21 @@
}
/**
+ * Processes a private command sent from the application to the TV input. This can be used
+ * to provide domain-specific features that are only known between certain TV inputs and
+ * their clients.
+ *
+ * @param action Name of the command to be performed. This <em>must</em> be a scoped name,
+ * i.e. prefixed with a package name you own, so that different developers will
+ * not create conflicting commands.
+ * @param data Any data to include with the command.
+ * @hide
+ */
+ @SystemApi
+ public void onAppPrivateCommand(String action, Bundle data) {
+ }
+
+ /**
* Called when an application requests to create an overlay view. Each session
* implementation can override this method and return its own view.
*
@@ -657,6 +753,13 @@
}
/**
+ * Calls {@link #onSetMainSession}.
+ */
+ void setMainSession(boolean isMainSession) {
+ onSetMainSession(isMainSession);
+ }
+
+ /**
* Calls {@link #onSetSurface}.
*/
void setSurface(Surface surface) {
@@ -726,6 +829,13 @@
}
/**
+ * Calls {@link #onAppPrivateCommand}.
+ */
+ void appPrivateCommand(String action, Bundle data) {
+ onAppPrivateCommand(action, data);
+ }
+
+ /**
* Creates an overlay view. This calls {@link #onCreateOverlayView} to get a view to attach
* to the overlay window.
*
@@ -877,16 +987,32 @@
@SuppressLint("HandlerLeak")
private final class ServiceHandler extends Handler {
private static final int DO_CREATE_SESSION = 1;
- private static final int DO_ADD_TV_INPUT_FROM_HARDWARE = 2;
- private static final int DO_REMOVE_TV_INPUT_FROM_HARDWARE = 3;
+ private static final int DO_ADD_HARDWARE_TV_INPUT = 2;
+ private static final int DO_REMOVE_HARDWARE_TV_INPUT = 3;
+ private static final int DO_ADD_HDMI_CEC_TV_INPUT = 4;
+ private static final int DO_REMOVE_HDMI_CEC_TV_INPUT = 5;
+ private static final int DO_SET_WRAPPED_TV_INPUT_ID = 6;
- private void broadcastAddTvInput(TvInputInfo inputInfo) {
+ private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
int n = mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
try {
- mCallbacks.getBroadcastItem(i).addTvInput(inputInfo);
+ mCallbacks.getBroadcastItem(i).addHardwareTvInput(deviceId, inputInfo);
} catch (RemoteException e) {
- Log.e(TAG, "Error while broadcasting: " + e);
+ Log.e(TAG, "Error while broadcasting.", e);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ }
+
+ private void broadcastAddHdmiCecTvInput(
+ int logicalAddress, TvInputInfo inputInfo) {
+ int n = mCallbacks.beginBroadcast();
+ for (int i = 0; i < n; ++i) {
+ try {
+ mCallbacks.getBroadcastItem(i).addHdmiCecTvInput(logicalAddress, inputInfo);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error while broadcasting.", e);
}
}
mCallbacks.finishBroadcast();
@@ -898,7 +1024,19 @@
try {
mCallbacks.getBroadcastItem(i).removeTvInput(inputId);
} catch (RemoteException e) {
- Log.e(TAG, "Error while broadcasting: " + e);
+ Log.e(TAG, "Error while broadcasting.", e);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ }
+
+ private void broadcastSetWrappedTvInputId(String inputId, String wrappedInputId) {
+ int n = mCallbacks.beginBroadcast();
+ for (int i = 0; i < n; ++i) {
+ try {
+ mCallbacks.getBroadcastItem(i).setWrappedInputId(inputId, wrappedInputId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error while broadcasting.", e);
}
}
mCallbacks.finishBroadcast();
@@ -929,22 +1067,45 @@
args.recycle();
return;
}
- case DO_ADD_TV_INPUT_FROM_HARDWARE: {
+ case DO_ADD_HARDWARE_TV_INPUT: {
TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj;
TvInputInfo inputInfo = onHardwareAdded(hardwareInfo);
if (inputInfo != null) {
- broadcastAddTvInput(inputInfo);
+ broadcastAddHardwareTvInput(hardwareInfo.getDeviceId(), inputInfo);
}
return;
}
- case DO_REMOVE_TV_INPUT_FROM_HARDWARE: {
- int deviceId = msg.arg1;
- String inputId = onHardwareRemoved(deviceId);
+ case DO_REMOVE_HARDWARE_TV_INPUT: {
+ TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj;
+ String inputId = onHardwareRemoved(hardwareInfo);
if (inputId != null) {
broadcastRemoveTvInput(inputId);
}
return;
}
+ case DO_ADD_HDMI_CEC_TV_INPUT: {
+ HdmiCecDeviceInfo cecDeviceInfo = (HdmiCecDeviceInfo) msg.obj;
+ TvInputInfo inputInfo = onHdmiCecDeviceAdded(cecDeviceInfo);
+ if (inputInfo != null) {
+ broadcastAddHdmiCecTvInput(cecDeviceInfo.getLogicalAddress(), inputInfo);
+ }
+ return;
+ }
+ case DO_REMOVE_HDMI_CEC_TV_INPUT: {
+ HdmiCecDeviceInfo cecDeviceInfo = (HdmiCecDeviceInfo) msg.obj;
+ String inputId = onHdmiCecDeviceRemoved(cecDeviceInfo);
+ if (inputId != null) {
+ broadcastRemoveTvInput(inputId);
+ }
+ return;
+ }
+ case DO_SET_WRAPPED_TV_INPUT_ID: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String inputId = (String) args.arg1;
+ String wrappedInputId = (String) args.arg2;
+ broadcastSetWrappedTvInputId(inputId, wrappedInputId);
+ return;
+ }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
return;
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 4966e27..5fe9955 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.SystemApi;
import android.content.Context;
import android.graphics.Rect;
import android.media.tv.TvInputManager.Session;
@@ -61,6 +62,9 @@
private static final int VIDEO_SIZE_VALUE_UNKNOWN = 0;
+ private static final Object sMainTvViewLock = new Object();
+ private static TvView sMainTvView;
+
private final Handler mHandler = new Handler();
private Session mSession;
private final SurfaceView mSurfaceView;
@@ -160,6 +164,29 @@
}
/**
+ * Set this as main TvView.
+ * <p>
+ * Main TvView is the TvView which user is watching and interacting mainly. It is used for
+ * determining internal behavior of hardware TV input devices. For example, this influences
+ * how HDMI-CEC active source will be managed.
+ * </p><p>
+ * First tuned TvView becomes main automatically, and keeps to be main until setMainTvView() is
+ * called for other TvView. Note that main TvView won't be reset even when current main TvView
+ * is removed from view hierarchy.
+ * </p>
+ * @hide
+ */
+ @SystemApi
+ public void setMainTvView() {
+ synchronized (sMainTvViewLock) {
+ sMainTvView = this;
+ if (hasWindowFocus() && mSession != null) {
+ mSession.setMainSession();
+ }
+ }
+ }
+
+ /**
* Sets the relative stream volume of this session to handle a change of audio focus.
*
* @param volume A volume value between 0.0f to 1.0f.
@@ -186,6 +213,11 @@
if (TextUtils.isEmpty(inputId)) {
throw new IllegalArgumentException("inputId cannot be null or an empty string");
}
+ synchronized (sMainTvViewLock) {
+ if (sMainTvView == null) {
+ sMainTvView = this;
+ }
+ }
if (mSessionCallback != null && mSessionCallback.mInputId.equals(inputId)) {
if (mSession != null) {
mSession.tune(channelUri);
@@ -229,6 +261,7 @@
* @see TvInputService.Session#dispatchContentBlocked(TvContentRating)
* @hide
*/
+ @SystemApi
public void requestUnblockContent(TvContentRating unblockedRating) {
if (mSession != null) {
mSession.requestUnblockContent(unblockedRating);
@@ -291,6 +324,26 @@
}
/**
+ * Call {@link TvInputService.Session#appPrivateCommand(String, Bundle)
+ * TvInputService.Session.appPrivateCommand()} on the current TvView.
+ *
+ * @param action Name of the command to be performed. This <em>must</em> be a scoped name, i.e.
+ * prefixed with a package name you own, so that different developers will not create
+ * conflicting commands.
+ * @param data Any data to include with the command.
+ * @hide
+ */
+ @SystemApi
+ public void sendAppPrivateCommand(String action, Bundle data) {
+ if (TextUtils.isEmpty(action)) {
+ throw new IllegalArgumentException("action cannot be null or an empty string");
+ }
+ if (mSession != null) {
+ mSession.sendAppPrivateCommand(action, data);
+ }
+ }
+
+ /**
* Dispatches an unhandled input event to the next receiver.
* <p>
* Except system keys, TvView always consumes input events in the normal flow. This is called
@@ -391,6 +444,18 @@
}
@Override
+ public void dispatchWindowFocusChanged(boolean hasFocus) {
+ super.dispatchWindowFocusChanged(hasFocus);
+ // Other app may have shown its own main TvView.
+ // Set main again to regain main session.
+ synchronized (sMainTvViewLock) {
+ if (hasFocus && this == sMainTvView && mSession != null) {
+ mSession.setMainSession();
+ }
+ }
+ }
+
+ @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
createSessionOverlayView();
@@ -601,6 +666,7 @@
* @param eventArgs Optional arguments of the event.
* @hide
*/
+ @SystemApi
public void onEvent(String inputId, String eventType, Bundle eventArgs) {
}
}
@@ -642,6 +708,11 @@
}
mSession = session;
if (session != null) {
+ synchronized (sMainTvViewLock) {
+ if (hasWindowFocus() && TvView.this == sMainTvView) {
+ mSession.setMainSession();
+ }
+ }
// mSurface may not be ready yet as soon as starting an application.
// In the case, we don't send Session.setSurface(null) unnecessarily.
// setSessionSurface will be called in surfaceCreated.
diff --git a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
index bda3b6b..89b2893 100644
--- a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
+++ b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
@@ -32,10 +32,17 @@
jmethodID mPostEvent;
jclass mSoundPoolClass;
} fields;
-
static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
return (SoundPool*)env->GetLongField(thiz, fields.mNativeContext);
}
+static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
+struct audio_attributes_fields_t {
+ jfieldID fieldUsage; // AudioAttributes.mUsage
+ jfieldID fieldContentType; // AudioAttributes.mContentType
+ jfieldID fieldFlags; // AudioAttributes.mFlags
+ jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags
+};
+static audio_attributes_fields_t javaAudioAttrFields;
// ----------------------------------------------------------------------------
static jint
@@ -176,10 +183,30 @@
}
static jint
-android_media_SoundPool_SoundPoolImpl_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality)
+android_media_SoundPool_SoundPoolImpl_native_setup(JNIEnv *env, jobject thiz, jobject weakRef,
+ jint maxChannels, jobject jaa)
{
+ if (jaa == 0) {
+ ALOGE("Error creating SoundPool: invalid audio attributes");
+ return -1;
+ }
+
+ audio_attributes_t *paa = NULL;
+ // read the AudioAttributes values
+ paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+ const jstring jtags =
+ (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
+ const char* tags = env->GetStringUTFChars(jtags, NULL);
+ // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
+ strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+ env->ReleaseStringUTFChars(jtags, tags);
+ paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
+ paa->content_type =
+ (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
+ paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
+
ALOGV("android_media_SoundPool_SoundPoolImpl_native_setup");
- SoundPool *ap = new SoundPool(maxChannels, (audio_stream_type_t) streamType, srcQuality);
+ SoundPool *ap = new SoundPool(maxChannels, paa);
if (ap == NULL) {
return -1;
}
@@ -190,6 +217,10 @@
// set callback with weak reference
jobject globalWeakRef = env->NewGlobalRef(weakRef);
ap->setCallback(android_media_callback, globalWeakRef);
+
+ // audio attributes were copied in SoundPool creation
+ free(paa);
+
return 0;
}
@@ -270,7 +301,7 @@
(void *)android_media_SoundPool_SoundPoolImpl_setRate
},
{ "native_setup",
- "(Ljava/lang/Object;III)I",
+ "(Ljava/lang/Object;ILjava/lang/Object;)I",
(void*)android_media_SoundPool_SoundPoolImpl_native_setup
},
{ "release",
@@ -289,27 +320,27 @@
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed\n");
- goto bail;
+ return result;
}
assert(env != NULL);
clazz = env->FindClass(kClassPathName);
if (clazz == NULL) {
ALOGE("Can't find %s", kClassPathName);
- goto bail;
+ return result;
}
fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.mNativeContext == NULL) {
ALOGE("Can't find SoundPoolImpl.mNativeContext");
- goto bail;
+ return result;
}
fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.mPostEvent == NULL) {
ALOGE("Can't find android/media/SoundPoolImpl.postEventFromNative");
- goto bail;
+ return result;
}
// create a reference to class. Technically, we're leaking this reference
@@ -317,11 +348,29 @@
fields.mSoundPoolClass = (jclass) env->NewGlobalRef(clazz);
if (AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
- goto bail;
+ return result;
+
+ // Get the AudioAttributes class and fields
+ jclass audioAttrClass = env->FindClass(kAudioAttributesClassPathName);
+ if (audioAttrClass == NULL) {
+ ALOGE("Can't find %s", kAudioAttributesClassPathName);
+ return result;
+ }
+ jclass audioAttributesClassRef = (jclass)env->NewGlobalRef(audioAttrClass);
+ javaAudioAttrFields.fieldUsage = env->GetFieldID(audioAttributesClassRef, "mUsage", "I");
+ javaAudioAttrFields.fieldContentType
+ = env->GetFieldID(audioAttributesClassRef, "mContentType", "I");
+ javaAudioAttrFields.fieldFlags = env->GetFieldID(audioAttributesClassRef, "mFlags", "I");
+ javaAudioAttrFields.fieldFormattedTags =
+ env->GetFieldID(audioAttributesClassRef, "mFormattedTags", "Ljava/lang/String;");
+ env->DeleteGlobalRef(audioAttributesClassRef);
+ if (javaAudioAttrFields.fieldUsage == NULL || javaAudioAttrFields.fieldContentType == NULL
+ || javaAudioAttrFields.fieldFlags == NULL
+ || javaAudioAttrFields.fieldFormattedTags == NULL) {
+ ALOGE("Can't initialize AudioAttributes fields");
+ return result;
+ }
/* success -- return valid version number */
- result = JNI_VERSION_1_4;
-
-bail:
- return result;
+ return JNI_VERSION_1_4;
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index 150ab74..02a441b 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -16,9 +16,11 @@
package com.android.keyguard;
+import android.app.AlarmClockInfo;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
+import android.provider.AlarmClock;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.AttributeSet;
@@ -121,15 +123,24 @@
void refreshAlarmStatus() {
// Update Alarm status
- String nextAlarm = mLockPatternUtils.getNextAlarm();
- if (!TextUtils.isEmpty(nextAlarm)) {
- mAlarmStatusView.setText(nextAlarm);
+ AlarmClockInfo nextAlarm = mLockPatternUtils.getNextAlarm();
+ if (nextAlarm != null) {
+ mAlarmStatusView.setText(formatNextAlarm(nextAlarm));
mAlarmStatusView.setVisibility(View.VISIBLE);
} else {
mAlarmStatusView.setVisibility(View.GONE);
}
}
+ String formatNextAlarm(AlarmClockInfo info) {
+ if (info == null) {
+ return "";
+ }
+ String skeleton = DateFormat.is24HourFormat(mContext) ? "EHm" : "Ehma";
+ String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+ return DateFormat.format(pattern, info.getTriggerTime()).toString();
+ }
+
private void updateOwnerInfo() {
if (mOwnerInfo == null) return;
String ownerInfo = getOwnerInfo();
diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk
index a3a1f8c..3fbd4d8 100644
--- a/packages/PrintSpooler/Android.mk
+++ b/packages/PrintSpooler/Android.mk
@@ -22,8 +22,6 @@
LOCAL_PACKAGE_NAME := PrintSpooler
-LOCAL_JAVA_LIBRARIES := framework-base
-
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 android-support-v7-recyclerview
include $(BUILD_PACKAGE)
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_menu_savetopdf.png b/packages/PrintSpooler/res/drawable-hdpi/ic_menu_savetopdf.png
new file mode 100644
index 0000000..639a63f
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-hdpi/ic_menu_savetopdf.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_menu_savetopdf.png b/packages/PrintSpooler/res/drawable-mdpi/ic_menu_savetopdf.png
new file mode 100644
index 0000000..ce6d75b
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-mdpi/ic_menu_savetopdf.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_menu_savetopdf.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_menu_savetopdf.png
new file mode 100644
index 0000000..98872f4
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xhdpi/ic_menu_savetopdf.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxhdpi/ic_menu_savetopdf.png b/packages/PrintSpooler/res/drawable-xxhdpi/ic_menu_savetopdf.png
new file mode 100644
index 0000000..4fba4e0
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxhdpi/ic_menu_savetopdf.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_menu_savetopdf.png b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_menu_savetopdf.png
new file mode 100644
index 0000000..42d8672
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_menu_savetopdf.png
Binary files differ
diff --git a/packages/PrintSpooler/res/values-cs/strings.xml b/packages/PrintSpooler/res/values-cs/strings.xml
index aee5adc..1b84568 100644
--- a/packages/PrintSpooler/res/values-cs/strings.xml
+++ b/packages/PrintSpooler/res/values-cs/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"Velikost papíru:"</string>
<string name="label_color" msgid="1108690305218188969">"Barva"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientace"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"Stránky"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Vše: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+ <string name="template_page_range" msgid="428638530038286328">"Rozsah: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"např. 1–5, 8, 11–13"</string>
<string name="print_preview" msgid="8010217796057763343">"Náhled tisku"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"Nainstalovat prohlížeč PDF (umožní náhled)"</string>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index 7212319..9b72da1 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"Taille du papier :"</string>
<string name="label_color" msgid="1108690305218188969">"Couleur"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"Pages"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Toutes (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
+ <string name="template_page_range" msgid="428638530038286328">"Plage de <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"p. ex. 1-5, 8, 11-13"</string>
<string name="print_preview" msgid="8010217796057763343">"Aperçu avant impression"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"Installer un lecteur PDF pour voir l\'aperçu"</string>
diff --git a/packages/PrintSpooler/res/values-hr/strings.xml b/packages/PrintSpooler/res/values-hr/strings.xml
index 58f761d..3b82f85 100644
--- a/packages/PrintSpooler/res/values-hr/strings.xml
+++ b/packages/PrintSpooler/res/values-hr/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"Veličina papira:"</string>
<string name="label_color" msgid="1108690305218188969">"U boji"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orijentacija"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"Stranice"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Sve stranice (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
+ <string name="template_page_range" msgid="428638530038286328">"Raspon od <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"npr. 1 – 5,8,11 – 13"</string>
<string name="print_preview" msgid="8010217796057763343">"Pregled ispisa"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"Instaliraj PDF preglednik za pregled"</string>
diff --git a/packages/PrintSpooler/res/values-hy-rAM/strings.xml b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
index a688d35..f77a7e8 100644
--- a/packages/PrintSpooler/res/values-hy-rAM/strings.xml
+++ b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"Թղթի չափը՝"</string>
<string name="label_color" msgid="1108690305218188969">"Գույնը"</string>
<string name="label_orientation" msgid="2853142581990496477">"Դիրքավորում"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"Էջեր"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Բոլորը՝ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+ <string name="template_page_range" msgid="428638530038286328">"Միջակայքը՝ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"օր.՝ 1-5, 8, 11-13"</string>
<string name="print_preview" msgid="8010217796057763343">"Տպելու նախադիտում"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"Նախադիտման համար տեղադրեք PDF դիտարկիչ"</string>
diff --git a/packages/PrintSpooler/res/values-ka-rGE/strings.xml b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
index a6200fa..d885765 100644
--- a/packages/PrintSpooler/res/values-ka-rGE/strings.xml
+++ b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"ფურცლის ზომა:"</string>
<string name="label_color" msgid="1108690305218188969">"ფერი"</string>
<string name="label_orientation" msgid="2853142581990496477">"ორიენტაცია"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"გვერდები"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"ყველა <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+ <string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g>-ის არეალი"</string>
<string name="pages_range_example" msgid="8558694453556945172">"მაგ. 1–5, 8, 11–13"</string>
<string name="print_preview" msgid="8010217796057763343">"ნახვა ამობეჭდვამდე"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"გადახედვისთვის დააყენეთ PDF მნახველი"</string>
diff --git a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
index 0ab170e..8c6a34fd 100644
--- a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
+++ b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"Қағаз өлшемі:"</string>
<string name="label_color" msgid="1108690305218188969">"Түс"</string>
<string name="label_orientation" msgid="2853142581990496477">"Бағыты"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"Беттер"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Барлық <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+ <string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> ауқымы"</string>
<string name="pages_range_example" msgid="8558694453556945172">"мысалы, 1—5,8,11—13"</string>
<string name="print_preview" msgid="8010217796057763343">"Алдын ала көру"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"Алдын ала көру мүмкін болу үшін PDF шолушыны орнатыңыз"</string>
diff --git a/packages/PrintSpooler/res/values-ky-rKG/strings.xml b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
index c14b47f..f60f994 100644
--- a/packages/PrintSpooler/res/values-ky-rKG/strings.xml
+++ b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"Барактын өлчөмү:"</string>
<string name="label_color" msgid="1108690305218188969">"Түс"</string>
<string name="label_orientation" msgid="2853142581990496477">"Багыттоо"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"Баракчалар"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Бардыгы <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+ <string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> аралыгы"</string>
<string name="pages_range_example" msgid="8558694453556945172">"мис. 1—5,8,11—13"</string>
<string name="print_preview" msgid="8010217796057763343">"Алдын ала көрүү"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"Алдын ала көрүү үчүн PDF-серепчи орнотуңуз"</string>
diff --git a/packages/PrintSpooler/res/values-ms-rMY/strings.xml b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
index 5ee274f..f327334 100644
--- a/packages/PrintSpooler/res/values-ms-rMY/strings.xml
+++ b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"Saiz kertas:"</string>
<string name="label_color" msgid="1108690305218188969">"Warna"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientasi"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"Halaman"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Semua <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+ <string name="template_page_range" msgid="428638530038286328">"Julat <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"cth. 1—5,8,11—13"</string>
<string name="print_preview" msgid="8010217796057763343">"Pratonton cetak"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"Pasang pemapar PDF untuk pratonton"</string>
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index 3a8a231..6abc571 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"Размер бумаги:"</string>
<string name="label_color" msgid="1108690305218188969">"Печать"</string>
<string name="label_orientation" msgid="2853142581990496477">"Ориентация"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"Страницы"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Все <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+ <string name="template_page_range" msgid="428638530038286328">"Диапазон <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"напр., 1–5, 8, 11–13"</string>
<string name="print_preview" msgid="8010217796057763343">"Предварительный просмотр"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"Установить средство просмотра PDF"</string>
diff --git a/packages/PrintSpooler/res/values-si-rLK/strings.xml b/packages/PrintSpooler/res/values-si-rLK/strings.xml
index 6963050..f6cc5e2 100644
--- a/packages/PrintSpooler/res/values-si-rLK/strings.xml
+++ b/packages/PrintSpooler/res/values-si-rLK/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"කඩදාසියේ ප්රමාණය:"</string>
<string name="label_color" msgid="1108690305218188969">"වර්ණය"</string>
<string name="label_orientation" msgid="2853142581990496477">"දිශානතිය"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"පිටු"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"සියලුම <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+ <string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> පරාසය"</string>
<string name="pages_range_example" msgid="8558694453556945172">"උ.දා. 1—5,8,11—13"</string>
<string name="print_preview" msgid="8010217796057763343">"මුද්රණ පෙරදසුන"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"පෙරදසුන සඳහා PDF දක්වනය ස්ථාපනය කරන්න"</string>
diff --git a/packages/PrintSpooler/res/values-sk/strings.xml b/packages/PrintSpooler/res/values-sk/strings.xml
index 47b4f06..439aae4 100644
--- a/packages/PrintSpooler/res/values-sk/strings.xml
+++ b/packages/PrintSpooler/res/values-sk/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"Veľkosť papiera:"</string>
<string name="label_color" msgid="1108690305218188969">"Farba"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientácia"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"Strany"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Všetky: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+ <string name="template_page_range" msgid="428638530038286328">"Rozsah: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"napr. 1–5, 8, 11–13"</string>
<string name="print_preview" msgid="8010217796057763343">"Ukážka pred tlačou"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"Inštalovať zobrazovač PDF na zobrazenie ukážky"</string>
diff --git a/packages/PrintSpooler/res/values-sr/strings.xml b/packages/PrintSpooler/res/values-sr/strings.xml
index 7de3adf..de0dfac 100644
--- a/packages/PrintSpooler/res/values-sr/strings.xml
+++ b/packages/PrintSpooler/res/values-sr/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"Величина папира:"</string>
<string name="label_color" msgid="1108690305218188969">"Боја"</string>
<string name="label_orientation" msgid="2853142581990496477">"Положај"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"Странице"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Све странице (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
+ <string name="template_page_range" msgid="428638530038286328">"Опсег од <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"нпр. 1–5, 8, 11–13"</string>
<string name="print_preview" msgid="8010217796057763343">"Преглед пре штампања"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"Инсталирај PDF приказивач за преглед"</string>
diff --git a/packages/PrintSpooler/res/values-th/strings.xml b/packages/PrintSpooler/res/values-th/strings.xml
index 921bfca..d1d45fe 100644
--- a/packages/PrintSpooler/res/values-th/strings.xml
+++ b/packages/PrintSpooler/res/values-th/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"ขนาดของกระดาษ:"</string>
<string name="label_color" msgid="1108690305218188969">"สี"</string>
<string name="label_orientation" msgid="2853142581990496477">"การวางแนว"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"หน้า"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"ทั้ง <xliff:g id="PAGE_COUNT">%1$s</xliff:g> หน้า"</string>
+ <string name="template_page_range" msgid="428638530038286328">"ช่วง <xliff:g id="PAGE_COUNT">%1$s</xliff:g> หน้า"</string>
<string name="pages_range_example" msgid="8558694453556945172">"เช่น 1—5,8,11—13"</string>
<string name="print_preview" msgid="8010217796057763343">"ตัวอย่างก่อนพิมพ์"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"ติดตั้งโปรแกรมดู PDF เพื่อดูหน้าตัวอย่าง"</string>
diff --git a/packages/PrintSpooler/res/values-tl/strings.xml b/packages/PrintSpooler/res/values-tl/strings.xml
index d94627e..70365e7 100644
--- a/packages/PrintSpooler/res/values-tl/strings.xml
+++ b/packages/PrintSpooler/res/values-tl/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"Laki ng papel:"</string>
<string name="label_color" msgid="1108690305218188969">"Kulay"</string>
<string name="label_orientation" msgid="2853142581990496477">"Oryentasyon"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"Mga Page"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Lahat ng <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+ <string name="template_page_range" msgid="428638530038286328">"Hanay ng <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"hal. 1—5,8,11—13"</string>
<string name="print_preview" msgid="8010217796057763343">"Preview sa pag-print"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"Mag-install ng PDF viewer para sa pag-preview"</string>
diff --git a/packages/PrintSpooler/res/values-tr/strings.xml b/packages/PrintSpooler/res/values-tr/strings.xml
index fab011e..ea803f65 100644
--- a/packages/PrintSpooler/res/values-tr/strings.xml
+++ b/packages/PrintSpooler/res/values-tr/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"Kağıt boyutu:"</string>
<string name="label_color" msgid="1108690305218188969">"Renkli"</string>
<string name="label_orientation" msgid="2853142581990496477">"Sayfa yönü"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"Sayfa"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> sayfanın tamamı"</string>
+ <string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> sayfalık aralık"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ör. 1-5,8,11-13"</string>
<string name="print_preview" msgid="8010217796057763343">"Yazdırmayı önizle"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"Önizlemek için PDF görüntüleyici yükleyin"</string>
diff --git a/packages/PrintSpooler/res/values-ur-rPK/strings.xml b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
index 2471d8b..1ff57a7 100644
--- a/packages/PrintSpooler/res/values-ur-rPK/strings.xml
+++ b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"کاغذ کا سائز:"</string>
<string name="label_color" msgid="1108690305218188969">"رنگ"</string>
<string name="label_orientation" msgid="2853142581990496477">"سمت بندی"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"صفحات"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"سبھی <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+ <string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> کی رینج"</string>
<string name="pages_range_example" msgid="8558694453556945172">"مثلاً 1—5,8,11—13"</string>
<string name="print_preview" msgid="8010217796057763343">"پرنٹ کا پیش منظر"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"پیش منظر کیلئے PDF ناظر انسٹال کریں"</string>
diff --git a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
index 71787f3..8ab3410 100644
--- a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
+++ b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"Qog‘oz o‘lchami:"</string>
<string name="label_color" msgid="1108690305218188969">"Rang"</string>
<string name="label_orientation" msgid="2853142581990496477">"Joylashuv"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"Sahifalar"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Barchasi (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
+ <string name="template_page_range" msgid="428638530038286328">"O‘zgarish chegarasi (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
<string name="pages_range_example" msgid="8558694453556945172">"masalan: 1—5,8,11—13"</string>
<string name="print_preview" msgid="8010217796057763343">"Chop qilishni oldindan ko‘rish"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"PDF ko‘rsatuvchi dasturni o‘rnatish"</string>
diff --git a/packages/PrintSpooler/res/values-zh-rTW/strings.xml b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
index 3cf1173..cf452c9 100644
--- a/packages/PrintSpooler/res/values-zh-rTW/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
@@ -25,12 +25,9 @@
<string name="label_paper_size_summary" msgid="5668204981332138168">"紙張大小:"</string>
<string name="label_color" msgid="1108690305218188969">"色彩"</string>
<string name="label_orientation" msgid="2853142581990496477">"方向"</string>
- <!-- no translation found for label_pages (7768589729282182230) -->
- <skip />
- <!-- no translation found for template_all_pages (3322235982020148762) -->
- <skip />
- <!-- no translation found for template_page_range (428638530038286328) -->
- <skip />
+ <string name="label_pages" msgid="7768589729282182230">"頁面"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"全部 <xliff:g id="PAGE_COUNT">%1$s</xliff:g> 頁"</string>
+ <string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> 頁"</string>
<string name="pages_range_example" msgid="8558694453556945172">"例如:1—5,8,11—13"</string>
<string name="print_preview" msgid="8010217796057763343">"列印預覽"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"安裝預覽所需的 PDF 檢視器"</string>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index 3678cf2..c3ddad9 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -76,8 +76,13 @@
public void onPageContentAvailable(BitmapDrawable content);
}
- public PageContentRepository(Context context) {
- mRenderer = new AsyncRenderer(context);
+ public interface OnMalformedPdfFileListener {
+ public void onMalformedPdfFile();
+ }
+
+ public PageContentRepository(Context context,
+ OnMalformedPdfFileListener malformedPdfFileListener) {
+ mRenderer = new AsyncRenderer(context, malformedPdfFileListener);
mState = STATE_CLOSED;
if (DEBUG) {
Log.i(LOG_TAG, "STATE_CLOSED");
@@ -440,19 +445,24 @@
}
private static class AsyncRenderer {
+ private static final int MALFORMED_PDF_FILE_ERROR = -2;
+
private final Context mContext;
private final PageContentLruCache mPageContentCache;
private final ArrayMap<Integer, RenderPageTask> mPageToRenderTaskMap = new ArrayMap<>();
+ private final OnMalformedPdfFileListener mOnMalformedPdfFileListener;
+
private int mPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
// Accessed only by the executor thread.
private PdfRenderer mRenderer;
- public AsyncRenderer(Context context) {
+ public AsyncRenderer(Context context, OnMalformedPdfFileListener malformedPdfFileListener) {
mContext = context;
+ mOnMalformedPdfFileListener = malformedPdfFileListener;
ActivityManager activityManager = (ActivityManager)
mContext.getSystemService(Context.ACTIVITY_SERVICE);
@@ -474,13 +484,19 @@
mRenderer = new PdfRenderer(source);
return mRenderer.getPageCount();
} catch (IOException ioe) {
- throw new IllegalStateException("Cannot open PDF document");
+ Log.e(LOG_TAG, "Cannot open PDF document");
+ return MALFORMED_PDF_FILE_ERROR;
}
}
@Override
public void onPostExecute(Integer pageCount) {
- mPageCount = pageCount;
+ if (pageCount == MALFORMED_PDF_FILE_ERROR) {
+ mOnMalformedPdfFileListener.onMalformedPdfFile();
+ mPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
+ } else {
+ mPageCount = pageCount;
+ }
if (callback != null) {
callback.run();
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index 30808ba..e976936 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -32,7 +32,6 @@
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
-import android.widget.CheckBox;
import android.widget.TextView;
import com.android.printspooler.R;
import com.android.printspooler.model.PageContentRepository;
@@ -48,7 +47,8 @@
/**
* This class represents the adapter for the pages in the print preview list.
*/
-public final class PageAdapter extends Adapter {
+public final class PageAdapter extends Adapter implements
+ PageContentRepository.OnMalformedPdfFileListener{
private static final String LOG_TAG = "PageAdapter";
private static final int MAX_PREVIEW_PAGES_BATCH = 50;
@@ -75,7 +75,7 @@
private final Context mContext;
private final LayoutInflater mLayoutInflater;
- private final ContentUpdateRequestCallback mContentUpdateRequestCallback;
+ private final ContentCallbacks mCallbacks;
private final PageContentRepository mPageContentRepository;
private final PreviewArea mPreviewArea;
@@ -109,8 +109,9 @@
private int mPageContentWidth;
private int mPageContentHeight;
- public interface ContentUpdateRequestCallback {
- public void requestContentUpdate();
+ public interface ContentCallbacks {
+ public void onRequestContentUpdate();
+ public void onMalformedPdfFile();
}
public interface PreviewArea {
@@ -120,13 +121,12 @@
public void setPadding(int left, int top, int right, int bottom);
}
- public PageAdapter(Context context, ContentUpdateRequestCallback updateRequestCallback,
- PreviewArea previewArea) {
+ public PageAdapter(Context context, ContentCallbacks callbacks, PreviewArea previewArea) {
mContext = context;
- mContentUpdateRequestCallback = updateRequestCallback;
+ mCallbacks = callbacks;
mLayoutInflater = (LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
- mPageContentRepository = new PageContentRepository(context);
+ mPageContentRepository = new PageContentRepository(context, this);
mSelectedPageElevation = mContext.getResources().getDimension(
R.dimen.selected_page_elevation);
@@ -165,6 +165,11 @@
}
}
+ @Override
+ public void onMalformedPdfFile() {
+ mCallbacks.onMalformedPdfFile();
+ }
+
public void onOrientationChanged() {
mColumnCount = mContext.getResources().getInteger(
R.integer.preview_page_per_row_count);
@@ -199,7 +204,7 @@
// If we already requested all pages, just wait.
if (!Arrays.equals(ALL_PAGES_ARRAY, mRequestedPages)) {
mRequestedPages = ALL_PAGES_ARRAY;
- mContentUpdateRequestCallback.requestContentUpdate();
+ mCallbacks.onRequestContentUpdate();
}
return;
} else {
@@ -548,7 +553,7 @@
if (DEBUG) {
Log.i(LOG_TAG, "Requesting pages: " + Arrays.toString(mRequestedPages));
}
- mContentUpdateRequestCallback.requestContentUpdate();
+ mCallbacks.onRequestContentUpdate();
}
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index a01e45c..a1b1aec 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -92,11 +92,11 @@
import java.util.regex.Pattern;
public class PrintActivity extends Activity implements RemotePrintDocument.UpdateResultCallbacks,
- PrintErrorFragment.OnActionListener, PageAdapter.ContentUpdateRequestCallback,
+ PrintErrorFragment.OnActionListener, PageAdapter.ContentCallbacks,
OptionsStateChangeListener, OptionsStateController {
private static final String LOG_TAG = "PrintActivity";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID";
@@ -121,6 +121,7 @@
private static final int STATE_CREATE_FILE_FAILED = 4;
private static final int STATE_PRINTER_UNAVAILABLE = 5;
private static final int STATE_UPDATE_SLOW = 6;
+ private static final int STATE_PRINT_COMPLETED = 7;
private static final int UI_STATE_PREVIEW = 0;
private static final int UI_STATE_ERROR = 1;
@@ -304,6 +305,10 @@
spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_QUEUED, null);
} break;
+ case STATE_PRINT_COMPLETED: {
+ spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_COMPLETED, null);
+ } break;
+
case STATE_CREATE_FILE_FAILED: {
spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_FAILED,
getString(R.string.print_write_error_message));
@@ -351,16 +356,26 @@
}
@Override
- public void requestContentUpdate() {
+ public void onRequestContentUpdate() {
if (canUpdateDocument()) {
updateDocument(true, false);
}
}
@Override
+ public void onMalformedPdfFile() {
+ mProgressMessageController.cancel();
+ ensureErrorUiShown(null, PrintErrorFragment.ACTION_RETRY);
+
+ setState(STATE_UPDATE_FAILED);
+
+ updateOptionsUi();
+ }
+
+ @Override
public void onActionPerformed() {
- if (mState == STATE_UPDATE_FAILED && canUpdateDocument()) {
- updateDocument(true, true);
+ if (mState == STATE_UPDATE_FAILED
+ && canUpdateDocument() && updateDocument(true, true)) {
ensurePreviewUiShown();
setState(STATE_CONFIGURING);
updateOptionsUi();
@@ -503,18 +518,15 @@
switch (requestCode) {
case ACTIVITY_REQUEST_CREATE_FILE: {
onStartCreateDocumentActivityResult(resultCode, data);
- }
- break;
+ } break;
case ACTIVITY_REQUEST_SELECT_PRINTER: {
onSelectPrinterActivityResult(resultCode, data);
- }
- break;
+ } break;
case ACTIVITY_REQUEST_POPULATE_ADVANCED_PRINT_OPTIONS: {
onAdvancedPrintOptionsActivityResult(resultCode, data);
- }
- break;
+ } break;
}
}
@@ -532,6 +544,8 @@
private void onStartCreateDocumentActivityResult(int resultCode, Intent data) {
if (resultCode == RESULT_OK && data != null) {
+ setState(STATE_PRINT_COMPLETED);
+ updateOptionsUi();
Uri uri = data.getData();
mPrintedDocument.writeContent(getContentResolver(), uri);
// Calling finish here does not invoke lifecycle callbacks but we
@@ -699,7 +713,8 @@
private static boolean isFinalState(int state) {
return state == STATE_PRINT_CONFIRMED
- || state == STATE_PRINT_CANCELED;
+ || state == STATE_PRINT_CANCELED
+ || state == STATE_PRINT_COMPLETED;
}
private void updateSelectedPagesFromPreview() {
@@ -1053,6 +1068,7 @@
}
if (mState == STATE_PRINT_CONFIRMED
+ || mState == STATE_PRINT_COMPLETED
|| mState == STATE_PRINT_CANCELED
|| mState == STATE_UPDATE_FAILED
|| mState == STATE_CREATE_FILE_FAILED
@@ -1289,7 +1305,7 @@
if (mDestinationSpinnerAdapter.getPdfPrinter() != mCurrentPrinter) {
mPrintButton.setImageResource(com.android.internal.R.drawable.ic_print);
} else {
- mPrintButton.setImageResource(com.android.internal.R.drawable.ic_menu_save);
+ mPrintButton.setImageResource(R.drawable.ic_menu_savetopdf);
}
if ((mRangeOptionsSpinner.getSelectedItemPosition() == 1
&& (TextUtils.isEmpty(mPageRangeEditText.getText()) || hasErrors()))
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 56f5a3a..6f41565 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -89,6 +89,7 @@
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN" />
<uses-permission android:name="android.permission.FRAME_STATS" />
+ <uses-permission android:name="android.permission.BIND_APPWIDGET" />
<application android:label="@string/app_label">
<provider
diff --git a/packages/Shell/res/values-tl/strings.xml b/packages/Shell/res/values-tl/strings.xml
index c5f18975..8d42ecd 100644
--- a/packages/Shell/res/values-tl/strings.xml
+++ b/packages/Shell/res/values-tl/strings.xml
@@ -18,8 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_finished_title" msgid="2293711546892863898">"Na-capture ang ulat ng bug"</string>
- <!-- no translation found for bugreport_finished_text (8389172248433597683) -->
- <skip />
+ <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Mag-swipe pakaliwa upang ibahagi ang iyong ulat ng bug"</string>
<string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Pindutin upang ibahagi ang iyong ulat ng bug"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Naglalaman ang mga ulat ng bug ng data mula sa iba\'t ibang file ng log ng system, kabilang ang personal at pribadong impormasyon. Magbahagi lang ng mga ulat ng bug sa apps at mga tao na pinagkakatiwalaan mo."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Ipakita ang mensaheng ito sa susunod"</string>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a038899..5ce3579 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -74,6 +74,7 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
<!-- WindowManager -->
<uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
@@ -154,6 +155,13 @@
</intent-filter>
</receiver>
+ <receiver android:name=".qs.tiles.HotspotTile$APChangedReceiver"
+ androidprv:primaryUserOnly="true">
+ <intent-filter>
+ <action android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
+ </intent-filter>
+ </receiver>
+
<activity android:name=".usb.UsbStorageActivity"
android:label="@*android:string/usb_storage_activity_title"
android:excludeFromRecents="true">
@@ -315,6 +323,19 @@
</intent-filter>
</activity>
+ <activity
+ android:name=".settings.BrightnessDialog"
+ android:label="@string/quick_settings_brightness_dialog_title"
+ android:theme="@android:style/Theme.DeviceDefault.Light.Dialog"
+ android:finishOnCloseSystemDialogs="true"
+ android:launchMode="singleInstance"
+ android:excludeFromRecents="true"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.SHOW_BRIGHTNESS_DIALOG" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
<!-- I dream of notifications -->
<service
diff --git a/packages/SystemUI/res/drawable/ic_chevron_left.xml b/packages/SystemUI/res/drawable/ic_chevron_left.xml
new file mode 100644
index 0000000..27c2034
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_chevron_left.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="24dp"
+ android:height="24dp"/>
+
+ <viewport
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0"/>
+
+ <path
+ android:fill="#ffffffff"
+ android:pathData="M23.1,11.1l-2.1000004,-2.1000004 -9.0,9.0 9.0,9.0 2.1000004,-2.1000004 -6.8999996,-6.8999996z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_hotspot_off.xml b/packages/SystemUI/res/drawable/ic_qs_hotspot_off.xml
index 7a62212..0a00d14 100644
--- a/packages/SystemUI/res/drawable/ic_qs_hotspot_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_hotspot_off.xml
@@ -15,16 +15,14 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="64dp"
- android:height="64dp"/>
+ android:width="64.0dp"
+ android:height="64.0dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
- android:fill="#00000000"
- android:stroke="#CCCCCC"
- android:strokeWidth="1.0"
- android:pathData="M12.0,11.0c-1.1,0.0 -2.0,0.9 -2.0,2.0c0.0,1.1 0.9,2.0 2.0,2.0c1.1,0.0 2.0,-0.9 2.0,-2.0C14.0,11.9 13.1,11.0 12.0,11.0zM18.0,13.0c0.0,-3.3 -2.7,-6.0 -6.0,-6.0c-3.3,0.0 -6.0,2.7 -6.0,6.0c0.0,2.2 1.2,4.1 3.0,5.2l1.0,-1.7c-1.2,-0.7 -2.0,-2.0 -2.0,-3.4c0.0,-2.2 1.8,-4.0 4.0,-4.0s4.0,1.8 4.0,4.0c0.0,1.5 -0.8,2.8 -2.0,3.4l1.0,1.7C16.8,17.1 18.0,15.2 18.0,13.0zM12.0,3.0C6.5,3.0 2.0,7.5 2.0,13.0c0.0,3.7 2.0,6.9 5.0,8.6l1.0,-1.7c-2.4,-1.4 -4.0,-4.0 -4.0,-6.9c0.0,-4.4 3.6,-8.0 8.0,-8.0s8.0,3.6 8.0,8.0c0.0,3.0 -1.6,5.5 -4.0,6.9l1.0,1.7c3.0,-1.7 5.0,-5.0 5.0,-8.6C22.0,7.5 17.5,3.0 12.0,3.0z"/>
+ android:fill="#4DFFFFFF"
+ android:pathData="M35.099998,28.500000c0.600000,-1.400000 0.900000,-2.900000 0.900000,-4.500000c0.000000,-6.600000 -5.400000,-12.000000 -12.000000,-12.000000c-1.600000,0.000000 -3.100000,0.300000 -4.500000,0.900000l3.200000,3.200000c0.400000,-0.100000 0.800000,-0.100000 1.200000,-0.100000c4.400000,0.000000 8.000000,3.600000 8.000000,8.000000c0.000000,0.400000 0.000000,0.800000 -0.100000,1.300000L35.099998,28.500000zM24.000000,8.000000c8.800000,0.000000 16.000000,7.200000 16.000000,16.000000c0.000000,2.700000 -0.700000,5.200000 -1.900000,7.500000l2.900000,2.900000c1.900000,-3.000000 3.000000,-6.600000 3.000000,-10.400000c0.000000,-11.000000 -9.000000,-20.000000 -20.000000,-20.000000c-3.800000,0.000000 -7.400000,1.100000 -10.400000,2.900000l2.900000,2.900000C18.700001,8.700000 21.299999,8.000000 24.000000,8.000000zM6.500000,5.000000L4.000000,7.500000l4.200000,4.200000C5.600000,15.100000 4.000000,19.400000 4.000000,24.000000c0.000000,7.400000 4.000000,13.800000 10.000000,17.299999l2.000000,-3.500000c-4.800000,-2.800000 -8.000000,-7.900000 -8.000000,-13.800000c0.000000,-3.500000 1.100000,-6.800000 3.100000,-9.400000l2.900000,2.900000C12.700000,19.400000 12.000000,21.600000 12.000000,24.000000c0.000000,4.400000 2.400000,8.300000 6.000000,10.400000l2.000000,-3.500000c-2.400000,-1.400000 -4.000000,-3.900000 -4.000000,-6.900000c0.000000,-1.300000 0.300000,-2.500000 0.900000,-3.600000l3.200000,3.200000c0.000000,0.100000 0.000000,0.300000 0.000000,0.400000c0.000000,2.200000 1.800000,4.000000 4.000000,4.000000c0.100000,0.000000 0.300000,0.000000 0.400000,0.000000l0.000000,0.000000l0.000000,0.000000l15.000000,15.000000l2.500000,-2.500000L8.500000,7.000000L6.500000,5.000000z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_hotspot_on.xml b/packages/SystemUI/res/drawable/ic_qs_hotspot_on.xml
index 3ccdd81..01cb0ab 100644
--- a/packages/SystemUI/res/drawable/ic_qs_hotspot_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_hotspot_on.xml
@@ -15,14 +15,14 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="64dp"
- android:height="64dp"/>
+ android:width="64.0dp"
+ android:height="64.0dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
android:fill="#FFFFFFFF"
- android:pathData="M12.0,11.0c-1.1,0.0 -2.0,0.9 -2.0,2.0c0.0,1.1 0.9,2.0 2.0,2.0c1.1,0.0 2.0,-0.9 2.0,-2.0C14.0,11.9 13.1,11.0 12.0,11.0zM18.0,13.0c0.0,-3.3 -2.7,-6.0 -6.0,-6.0c-3.3,0.0 -6.0,2.7 -6.0,6.0c0.0,2.2 1.2,4.1 3.0,5.2l1.0,-1.7c-1.2,-0.7 -2.0,-2.0 -2.0,-3.4c0.0,-2.2 1.8,-4.0 4.0,-4.0s4.0,1.8 4.0,4.0c0.0,1.5 -0.8,2.8 -2.0,3.4l1.0,1.7C16.8,17.1 18.0,15.2 18.0,13.0zM12.0,3.0C6.5,3.0 2.0,7.5 2.0,13.0c0.0,3.7 2.0,6.9 5.0,8.6l1.0,-1.7c-2.4,-1.4 -4.0,-4.0 -4.0,-6.9c0.0,-4.4 3.6,-8.0 8.0,-8.0s8.0,3.6 8.0,8.0c0.0,3.0 -1.6,5.5 -4.0,6.9l1.0,1.7c3.0,-1.7 5.0,-5.0 5.0,-8.6C22.0,7.5 17.5,3.0 12.0,3.0z"/>
+ android:pathData="M24.000000,22.000000c-2.200000,0.000000 -4.000000,1.800000 -4.000000,4.000000c0.000000,2.200000 1.800000,4.000000 4.000000,4.000000c2.200000,0.000000 4.000000,-1.800000 4.000000,-4.000000C28.000000,23.799999 26.200001,22.000000 24.000000,22.000000zM36.000000,26.000000c0.000000,-6.600000 -5.400000,-12.000000 -12.000000,-12.000000c-6.600000,0.000000 -12.000000,5.400000 -12.000000,12.000000c0.000000,4.400000 2.400000,8.300000 6.000000,10.400000l2.000000,-3.500000c-2.400000,-1.400000 -4.000000,-3.900000 -4.000000,-6.900000c0.000000,-4.400000 3.600000,-8.000000 8.000000,-8.000000s8.000000,3.600000 8.000000,8.000000c0.000000,3.000000 -1.600000,5.500000 -4.000000,6.900000l2.000000,3.500000C33.599998,34.299999 36.000000,30.400000 36.000000,26.000000zM24.000000,6.000000C13.000000,6.000000 4.000000,15.000000 4.000000,26.000000c0.000000,7.400000 4.000000,13.800000 10.000000,17.299999l2.000000,-3.500000c-4.800000,-2.800000 -8.000000,-7.900000 -8.000000,-13.800000c0.000000,-8.800000 7.200000,-16.000000 16.000000,-16.000000s16.000000,7.200000 16.000000,16.000000c0.000000,5.900000 -3.200000,11.100000 -8.000000,13.800000l2.000000,3.500000c6.000000,-3.500000 10.000000,-9.900000 10.000000,-17.299999C44.000000,15.000000 35.000000,6.000000 24.000000,6.000000z"/>
</vector>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index db5983b..42fb740 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -22,7 +22,7 @@
android:layout_height="match_parent"
android:layout_width="match_parent"
>
- <com.android.systemui.statusbar.AlphaImageView
+ <com.android.systemui.statusbar.KeyguardAffordanceView
android:id="@+id/camera_button"
android:layout_height="64dp"
android:layout_width="64dp"
@@ -32,7 +32,7 @@
android:scaleType="center"
android:contentDescription="@string/accessibility_camera_button" />
- <com.android.systemui.statusbar.AlphaImageView
+ <com.android.systemui.statusbar.KeyguardAffordanceView
android:id="@+id/phone_button"
android:layout_height="64dp"
android:layout_width="64dp"
@@ -52,7 +52,7 @@
android:textColor="#ffffff"
android:textAppearance="?android:attr/textAppearanceSmall"/>
- <com.android.systemui.statusbar.AlphaImageView
+ <com.android.systemui.statusbar.KeyguardAffordanceView
android:id="@+id/lock_icon"
android:layout_width="64dp"
android:layout_height="64dp"
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 526aa5e..403860c 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -40,6 +40,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
- android:textColor="@color/qs_user_detail_name" />
+ android:textColor="@color/qs_user_detail_name"
+ android:gravity="center_horizontal" />
</com.android.systemui.qs.tiles.UserDetailItemView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 3e4c1f6..93c5538 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -62,13 +62,12 @@
android:visibility="invisible"
android:src="@drawable/recents_dismiss_light" />
</com.android.systemui.recents.views.TaskBarView>
- <FrameLayout
+ <com.android.systemui.recents.views.TaskFooterView
android:id="@+id/lock_to_app"
android:layout_width="match_parent"
android:layout_height="@dimen/recents_task_view_lock_to_app_button_height"
android:layout_gravity="center_horizontal|bottom"
- android:background="@drawable/recents_lock_to_task_button_bg"
- android:visibility="invisible">
+ android:background="@drawable/recents_lock_to_task_button_bg">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
@@ -82,7 +81,7 @@
android:fontFamily="sans-serif-medium"
android:singleLine="true"
android:textAllCaps="true" />
- </FrameLayout>
+ </com.android.systemui.recents.views.TaskFooterView>
</com.android.systemui.recents.views.TaskView>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 00715b2..785894a 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Skermkiekie geneem."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Raak om jou skermkiekie te sien."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Kon nie skermkiekie neem nie."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Kon nie skermkiekie stoor nie. Geheue kan dalk in gebruik wees."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB-lêeroordrag-opsies"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Heg as \'n mediaspeler (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Heg as \'n kamera (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Laai tans (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> tot vol)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Gas"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ gas"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Laat gas uitgaan"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Een minuut lank"</item>
<item quantity="other" msgid="6924190729213550991">"%d minute lank"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Maak batteryspaarder se instellings oop"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Inhoud versteek"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Laat gas uitgaan"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sal alles begin vasvang wat op jou skerm gewys word."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Moenie weer wys nie"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Begin nou"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index d6e482b..4c9437f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"ቅጽበታዊ ገጽ እይታ ተቀርጿል"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"የአንተን ቅጽበታዊ ገጽ እይታ ለማየት ንካ"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ቅጽበታዊ ገጽ እይታ መቅረጽ አልተቻለም::"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"የማያ ፎቶማስቀመጥ አልተቻለም። ማከማቻም አገልግሎት ላይ ሊሆን ይችላል።"</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"የUSB ፋይል ሰደዳ አማራጮች"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"እንደ ማህደረ አጫዋች (MTP) ሰካ"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"እንደ ካሜራ (PTP) ሰካ"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ሃይል በመሙላት ላይ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> እስከሚሞላ ድረስ)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"እንግዳ"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ እንግዳ"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"እንግዳ ያስወጡ"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"ለአንድ ደቂቃ"</item>
<item quantity="other" msgid="6924190729213550991">"ለ%d ደቂቃዎች"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"የባትሪ ኃይል ቆጣቢ ቅንብሮች"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"ይዘቶች ተደብቀዋል"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"እንግዳ ያስወጡ"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> በማያ ገጽዎ ላይ የታየውን ነገር በሙሉ ማንሳት ይጀምራል።"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"ዳግመኛ አታሳይ"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"አሁን ጀምር"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index e75946a..ae9e305 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"تم التقاط لقطة الشاشة."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"المس لعرض لقطة الشاشة."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"تعذر التقاط لقطة الشاشة."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"تعذر حفظ لقطة الشاشة. قد يكون التخزين قيد الاستخدام."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"خيارات نقل الملفات عبر USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"تحميل كمشغل وسائط (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"تحميل ككاميرا (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"إغلاق اللوحة"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"وقت أكثر"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"وقت أقل"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2-3 غيغابايت من البيانات المعطلة"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4 غيغابايت من البيانات المعطلة"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"البيانات الخلوية معطلة"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"البيانات معطلة"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"عطل جهازك البيانات نظرًا لبلوغها الحد الذي تم تعيينه.\n\nعلمًا بأن إعادتها قد يؤدي إلى تحمل رسوم من قبل مشغّل شبكة الجوّال."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"تشغيل البيانات"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"لا يوجد اتصال إنترنت"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi متصل"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"جارٍ البحث عن GPS"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"بحث"</string>
<string name="description_direction_up" msgid="7169032478259485180">"تمرير لأعلى لـ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"تمرير لليسار لـ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"بدون إزعاج، بما في ذلك المنبهات"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"بدون مقاطعات"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"المقاطعات ذات الأولوية فقط"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"التنبيه المقبل في <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"التنبيه المقبل في <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"لن تسمع المنبهات في <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"الإشعارات الأقل إلحاحًا أدناه"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"انقر مرة أخرى للفتح"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"جارٍ الشحن (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> حتى الامتلاء)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"المدعو"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ مدعو"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"الخروج من وضع الضيف"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"لمدة دقيقة واحدة"</item>
<item quantity="other" msgid="6924190729213550991">"لمدة %d من الدقائق"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"فتح إعدادات وضع توفير الطاقة"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"المحتويات مخفية"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> سيبدأ التقاط كل شيء يتم عرضه على الشاشة."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"عدم الإظهار مرة أخرى"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"البدء الآن"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 32ace8e..a166296 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Екранната снимка е заснета."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Докоснете, за да видите екранната си снимка."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Екранната снимка не можа да бъде заснета."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Екранната снимка не можа да бъде запазена. Възможно е хранилището да се използва."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Опции за пренос на файлове чрез USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Свързване като медиен плейър (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Свързване като камера (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Зарежда се (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до пълно зареждане)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Гост"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ гост"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Изход от сесията като гост"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"За една минута"</item>
<item quantity="other" msgid="6924190729213550991">"За %d минути"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Отваряне на настройките за режима за запазване на батерията"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Скрито съдържание"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Изход от сесията като гост"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ще започне да заснема всичко, което се показва на екрана ви."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Да не се показва отново"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Стартиране сега"</string>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 0577ce2..7bdc06a 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"স্ক্রীনশট নেওয়া হযেছে৷"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"আপনার স্ক্রীনশট দেখতে স্পর্শ করুন৷"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"স্ক্রীনশট নেওয়া যায়নি৷"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"স্ক্রীনশট সংরক্ষণ করা যায়নি৷ সঞ্চয়স্থান ব্যবহারে থাকতে পারে৷"</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB ফাইল স্থানান্তরের বিকল্পগুলি"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"একটি মিডিয়া প্লেয়ার হিসাবে মাউন্ট করুন (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"একটি ক্যামেরা হিসাবে মাউন্ট করুন (PTP)"</string>
@@ -254,7 +255,7 @@
<string name="zen_important_interruptions" msgid="3477041776609757628">"শুধুমাত্র প্রাধান্য বাধাগুলি"</string>
<string name="zen_alarm_information_time" msgid="5235772206174372272">"আপনার পরবর্তী অ্যালার্মের সময় <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="zen_alarm_information_day_time" msgid="8422733576255047893">"আপনার পরবর্তী অ্যালার্মের সময় <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
- <string name="zen_alarm_warning" msgid="6873910860111498041">"<xliff:g id="ALARM_TIME">%s</xliff:g> বাজলে আপনার অ্যালার্ম শুনতে পাবেন না"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"<xliff:g id="ALARM_TIME">%s</xliff:g> বাজলে আপনি অ্যালার্ম শুনতে পাবেন না"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"নিচে অপেক্ষাকৃত কম জরুরী বিজ্ঞপ্তিগুলি"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"খোলার জন্য আবার আলতো চাপুন"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"চার্জ হচ্ছে (পূর্ণ হতে <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> সময় বাকি)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"অতিথি"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ অতিথি"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"অতিথির প্রস্থান"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"এক মিনিটের জন্য"</item>
<item quantity="other" msgid="6924190729213550991">"%d মিনিটের জন্য"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"ব্যাটারি সেভার সেটিংস খুলুন"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"লুকানো বিষয়বস্তু"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"অতিথির প্রস্থান"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> আপনার স্ক্রীনে দেখানো সব কিছু ক্যাপচার করা শুরু করবে।"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"আর দেখাবেন না"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"এখন শুরু করুন"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index d651aaa..541d60d 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"S\'ha fet una captura de pantalla."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Toca per veure la captura de pantalla."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"No s\'ha pogut fer una captura de pantalla."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"No s\'ha pogut desar la captura de pantalla. És possible que l\'emmagatzematge estigui en ús."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Opcions transf. fitxers USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Munta com a reproductor multimèdia (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Munta com a càmera (PTP)"</string>
@@ -270,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregant (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar la càrrega)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Convidat"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Convidat"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Surt del mode de convidat"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Durant un minut"</item>
<item quantity="other" msgid="6924190729213550991">"Durant %d minuts"</item>
@@ -283,7 +297,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Obre la configuració de la funció Estalvi de bateria"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contingut amagat"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Surt del mode de convidat"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> començarà a enregistrar tot el que es mostri a la pantalla."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"No ho tornis a mostrar"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Comença ara"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 3412c2a..68b84a9 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Snímek obrazovky zachycen."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Snímek obrazovky zobrazíte dotykem."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Snímek obrazovky se nepodařilo zachytit."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Snímek obrazovky se nepodařilo uložit. Je možné, že je externí úložiště právě používáno."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Možnosti přenosu souborů pomocí rozhraní USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Připojit jako přehrávač médií (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Připojit jako fotoaparát (PTP)"</string>
@@ -168,18 +169,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Zavřít panel"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Delší doba"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Kratší doba"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"Data 2G a 3G jsou vypnuta"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"Data 4G jsou vypnuta"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Mobilní data jsou vypnuta"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Data jsou vypnuta"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Zařízení datové přenosy vypnulo, protože dosáhlo limitu, který jste nastavili.\n\nJejich opětovné zapnutí může vézt k účtování poplatků operátorem."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Zapnout data"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Žádné přip. k internetu"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi: připojeno"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Vyhledávání satelitů GPS"</string>
@@ -257,16 +252,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Vyhledávání"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Přejeďte prstem nahoru: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
<string name="description_direction_left" msgid="7207478719805562165">"Přejeďte prstem doleva: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Žádná přerušení, ani budíky"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Žádná vyrušení"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Pouze prioritní vyrušení"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Další budík je nastaven na: <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Další budík: <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Neuslyšíte budík v <xliff:g id="ALARM_TIME">%s</xliff:g>."</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Méně urgentní oznámení níže"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Oznámení otevřete opětovným klepnutím"</string>
@@ -280,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nabíjení (plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Host"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"Přidat hosta"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Ukončit relaci hosta"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Na jednu minutu"</item>
<item quantity="other" msgid="6924190729213550991">"Na %d min"</item>
@@ -293,12 +297,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Otevřít nastavení režimu Úspora baterie"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g> %%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Skrytý obsah"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"Aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> začne zaznamenávat vše, co je zobrazeno na obrazovce."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Tuto zprávu příště nezobrazovat"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Spustit"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index dc62683..f915332 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Skærmbilledet er gemt."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Tryk for at se dit skærmbillede."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Skærmbilledet kunne ikke tages."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Skærmbilledet kunne ikke gemmes. Eksternt lager kan være i brug."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Muligheder for USB-filoverførsel"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Isæt som en medieafspiller (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Isæt som et kamera (PTP)"</string>
@@ -214,7 +215,7 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi slået fra"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Der er ingen tilgængelige gemte netværk"</string>
<string name="quick_settings_cast_title" msgid="1893629685050355115">"Cast skærm"</string>
- <string name="quick_settings_casting" msgid="6601710681033353316">"Casting"</string>
+ <string name="quick_settings_casting" msgid="6601710681033353316">"Caster"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Enhed uden navn"</string>
<string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Klar til at caste"</string>
<string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Der er ingen tilgængelige enheder"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Opladning (fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Gæst"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Gæst"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Forlad gæstesession"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"I ét minut"</item>
<item quantity="other" msgid="6924190729213550991">"I %d minutter"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Åbn indstillinger for Batteribesparende"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g> %%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Indholdet er skjult"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Forlad gæstesession"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vil begynde at optage alt, hvad der vises på din skærm."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Vis ikke igen"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Start nu"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index a57d3a7..d7213ba 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot aufgenommen"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Zum Ansehen berühren"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Screenshot konnte nicht aufgenommen werden."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Screenshot konnte nicht gespeichert werden. Eventuell wird der Speicher gerade verwendet."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB-Dateiübertragungsoptionen"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Als Medienplayer (MTP) bereitstellen"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Als Kamera (PTP) bereitstellen"</string>
@@ -168,11 +169,11 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Fenster schließen"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mehr Zeit"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Weniger Zeit"</string>
- <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G/3G-Daten sind deaktiviert"</string>
- <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G-Daten sind deaktiviert"</string>
- <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Mobilfunkdaten sind deaktiviert"</string>
- <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Daten sind deaktiviert"</string>
- <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Die Daten wurden auf Ihrem Gerät deaktiviert, da das von Ihnen festgelegte Limit erreicht wurde.\n\nWenn Sie die Daten erneut aktivieren, berechnet Ihr Mobilfunkanbieter möglicherweise Gebühren."</string>
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G/3G-Daten deaktiviert"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G-Daten deaktiviert"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Mobilfunkdaten deaktiviert"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Daten deaktiviert"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Die Datennutzung wurde auf Ihrem Gerät deaktiviert, da das von Ihnen festgelegte Limit erreicht wurde.\n\nWenn Sie die Funktion erneut aktivieren, berechnet Ihr Mobilfunkanbieter möglicherweise Gebühren."</string>
<string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Daten aktivieren"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Keine Internetverbindung"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"WLAN verbunden"</string>
@@ -270,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Wird aufgeladen (voll in <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Gast"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Gast"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Gastmodus beenden"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Für eine Minute"</item>
<item quantity="other" msgid="6924190729213550991">"Für %d Minuten"</item>
@@ -283,7 +297,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Einstellungen für den Energiesparmodus öffnen"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g> %%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Inhalte ausgeblendet"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Gastmodus beenden"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> nimmt alle auf Ihrem Bildschirm angezeigten Aktivitäten auf."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Nicht erneut anzeigen"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Jetzt starten"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index c913b72..c972a69 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Λήφθηκε το στιγμιότυπο οθόνης ."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Αγγίξτε για να δείτε το στιγμιότυπο οθόνης σας"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Αδύνατη η αποθήκευση του στιγμιότυπου οθόνης."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Αδύνατη η αποθήκευση στιγμιότυπου οθόνης. Ο εξωτερικός χώρος αποθήκευσης μπορεί να είναι σε χρήση."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Επιλογές μεταφοράς αρχείων μέσω USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Προσάρτηση ως μονάδας αναπαραγωγής μέσων (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Προσάρτηση ως κάμερας (PTP)"</string>
@@ -270,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Φόρτιση (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> για πλήρη φόρτιση)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Επισκέπτης"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Επισκέπτης"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Έξοδος επισκέπτη"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Για ένα λεπτό"</item>
<item quantity="other" msgid="6924190729213550991">"Για %d λεπτά"</item>
@@ -283,7 +297,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Άνοιγμα ρυθμίσεων Εξοικονόμησης μπαταρίας"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Κρυφό περιεχόμενο"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Έξοδος επισκέπτη"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"Θα ξεκινήσει η καταγραφή του περιεχομένου που εμφανίζεται στην οθόνη σας από την εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Να μην εμφανιστεί ξανά"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Έναρξη τώρα"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index b0981b1..e3fa73f 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Touch to view your screenshot."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Couldn\'t save screenshot. Storage may be in use."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Mount as a media player (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Mount as a camera (PTP)"</string>
@@ -223,7 +224,7 @@
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invert colours"</string>
<string name="quick_settings_color_space_label" msgid="853443689745584770">"Colour correction mode"</string>
<string name="quick_settings_more_settings" msgid="326112621462813682">"More settings"</string>
- <string name="quick_settings_done" msgid="3402999958839153376">"Finished"</string>
+ <string name="quick_settings_done" msgid="3402999958839153376">"Done"</string>
<string name="quick_settings_connected" msgid="1722253542984847487">"Connected"</string>
<string name="quick_settings_connecting" msgid="47623027419264404">"Connecting..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charging (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Guest"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Guest"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Exit guest"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"For one minute"</item>
<item quantity="other" msgid="6924190729213550991">"For %d minutes"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Open battery saver settings"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contents hidden"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Exit guest"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will start capturing everything that\'s displayed on your screen."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Don\'t show again"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Start now"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index b0981b1..e3fa73f 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Touch to view your screenshot."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Couldn\'t save screenshot. Storage may be in use."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Mount as a media player (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Mount as a camera (PTP)"</string>
@@ -223,7 +224,7 @@
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invert colours"</string>
<string name="quick_settings_color_space_label" msgid="853443689745584770">"Colour correction mode"</string>
<string name="quick_settings_more_settings" msgid="326112621462813682">"More settings"</string>
- <string name="quick_settings_done" msgid="3402999958839153376">"Finished"</string>
+ <string name="quick_settings_done" msgid="3402999958839153376">"Done"</string>
<string name="quick_settings_connected" msgid="1722253542984847487">"Connected"</string>
<string name="quick_settings_connecting" msgid="47623027419264404">"Connecting..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charging (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Guest"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Guest"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Exit guest"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"For one minute"</item>
<item quantity="other" msgid="6924190729213550991">"For %d minutes"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Open battery saver settings"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contents hidden"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Exit guest"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will start capturing everything that\'s displayed on your screen."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Don\'t show again"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Start now"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index e69eec9..078eb86 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Se guardó la captura de pantalla."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Toca para ver tu captura de pantalla."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"No se pudo guardar la captura de pantalla."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"No se pudo guardar la captura de pantalla. Puede que el almacenamiento esté en uso."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Opciones de transferencia de archivos por USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Activar como reproductor de medios (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Activar como cámara (PTP)"</string>
@@ -270,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Cargando (faltan <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para completar)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Invitado"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"Agregar invitado"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Salir de modo invitado"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Durante un minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Durante %d minutos"</item>
@@ -283,7 +297,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Abrir configuración del ahorro de batería"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contenidos ocultos"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Salir de modo invitado"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> comenzará la captura de todo lo que se muestre en la pantalla."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"No volver a mostrar"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Comenzar ahora"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 48fe8de..a4311b4 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Captura guardada"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Toca para ver la captura de pantalla"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"No se ha podido guardar la captura de pantalla."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"No se ha podido guardar la captura de pantalla. Puede que el almacenamiento esté en uso."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Opciones de transferencia de archivos por USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Activar como reproductor de medios (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Activar como cámara (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Cargando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hasta completar)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Invitado"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"Añadir invitado"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Salir de modo invitado"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Durante un minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Durante %d minutos"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Abrir ajustes de la función de ahorro de batería"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contenidos ocultos"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Salir de modo invitado"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> empezará a capturar todo lo que aparezca en la pantalla."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"No volver a mostrar"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Iniciar ahora"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 80fb802..01196bb 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Ekraanipilt on jäädvustatud."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Puudutage kuvatõmmise vaatamiseks."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Kuvatõmmist ei saanud jäädvustada."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Kuvatõmmist ei saa salvestada. Mäluseade võib olla kasutuses."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB-failiedastuse valikud"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Paigalda meediumimängijana (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Paigalda kaamerana (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Sule paneel"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Rohkem aega"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Vähem aega"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G–3G andmeside on väljalülitatud"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G andmeside on väljalülitatud"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Mob. andmeside väljalülitatud"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Andmeside on väljalülitatud"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Seade lülitas andmeside välja, sest teie määratud limiit on täis.\n\nKui lülitate andmeside uuesti sisse, siis võivad lisanduda operaatori tasud."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Lülita andmeside sisse"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Interneti-ühendus puudub"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"WiFi on ühendatud"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"GPS-i otsimine"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Otsing"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Lohistage üles: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Lohistage vasakule: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Ei mingeid katkestusi, k.a äratus"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Mitte ühtegi katkestust"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Ainult prioriteetsed katkestused"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Teie järgmine äratus on <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Teie järgmine äratus on <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Te ei kuule äratust <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Vähem kiireloomulised märguanded on allpool"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Avamiseks puudutage uuesti"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Laadimine (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>, kuni seade on täis)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Külaline"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ külaline"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Välju külastaja režiimist"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Üheks minutiks"</item>
<item quantity="other" msgid="6924190729213550991">"%d minutiks"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Ava akusäästja seaded"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Sisu on peidetud"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> hakkab jäädvustama kõike, mida ekraanil kuvatakse."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Ära kuva uuesti"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Alusta kohe"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index ddeadd9..15a96e0 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Pantaila-argazkia atera da."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Pantaila-argazkia ikusteko, ukitu ezazu."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Ezin izan da pantaila-argazkia atera."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Ezin izan da pantaila-argazkia gorde. Baliteke memoria erabiltzen aritzea."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB fitxategiak transferitzeko aukerak"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Muntatu multimedia-erreproduzigailu gisa (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Muntatu kamera gisa (PTP)"</string>
@@ -191,7 +192,7 @@
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetootha"</string>
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetootha (<xliff:g id="NUMBER">%d</xliff:g> gailu)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetootha desaktibatuta"</string>
- <string name="quick_settings_bluetooth_detail_empty_text" msgid="4910015762433302860">"Ez dago bikotetutako gailurik erabilgarri"</string>
+ <string name="quick_settings_bluetooth_detail_empty_text" msgid="4910015762433302860">"Ez dago parekatutako gailurik erabilgarri"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Distira"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Biratze automatikoa"</string>
<string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Biratzea blokeatuta"</string>
@@ -213,8 +214,8 @@
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ez dago sarerik"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi konexioa desaktibatuta"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Ez dago gordetako sarerik erabilgarri"</string>
- <string name="quick_settings_cast_title" msgid="1893629685050355115">"Igorri pantaila"</string>
- <string name="quick_settings_casting" msgid="6601710681033353316">"Igorpena"</string>
+ <string name="quick_settings_cast_title" msgid="1893629685050355115">"Igorri pantailako edukia"</string>
+ <string name="quick_settings_casting" msgid="6601710681033353316">"Igortzen"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Izenik gabeko gailua"</string>
<string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Igortzeko prest"</string>
<string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Ez dago gailurik erabilgarri"</string>
@@ -236,7 +237,7 @@
<string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"Mugaren gainetik"</string>
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> erabilita"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Muga: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> abisua"</string>
+ <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Abisua: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="recents_empty_message" msgid="7883614615463619450">"Ez dago azkenaldian erabilitako aplikaziorik"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Aplikazioaren informazioa"</string>
<string name="recents_lock_to_app_button_label" msgid="4793991421811647489">"aplikazio bakarreko modua"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Kargatzen (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> guztiz kargatu arte)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Gonbidatua"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Gonbidatua"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Irten gonbidatuen modutik"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Minutu batez"</item>
<item quantity="other" msgid="6924190729213550991">"%d minutuz"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Ireki bateria aurrezlearen ezarpenak"</string>
<string name="battery_level_template" msgid="1609636980292580020">"%% <xliff:g id="LEVEL">%d</xliff:g>"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Edukiak ezkutatuta daude"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Irten gonbidatuen modutik"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak pantailan bistaratzen den guztia grabatuko du."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ez erakutsi berriro"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Hasi"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 214253a..73a5800 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"تصویر صفحه گرفته شد."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"برای مشاهده تصویر صفحه خود، لمس کنید."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"تصویر صفحه گرفته نشد."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"تصویر صفحه ذخیره نشد. ممکن است دستگاه ذخیره در حال استفاده باشد."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"گزینههای انتقال فایل USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"نصب بهعنوان دستگاه پخش رسانه (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"تصب بهعنوان دوربین (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"بستن پانل"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"زمان بیشتر"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"زمان کمتر"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"داده 2G-3G خاموش است"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"داده 4G خاموش است"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"داده شبکه سلولی خاموش است"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"داده خاموش است"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"دستگاه شما خاموش شد زیرا به حد تنظیم شده توسط شما رسید.\n\nروشن کردن مجدد آن میتواند به هزینههایی از طرف شرکت مخابراتی شما منجر شود."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"روشن کردن داده"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"اتصال اینترنتی وجود ندارد"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi متصل شد"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"جستجو برای GPS"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"جستجو"</string>
<string name="description_direction_up" msgid="7169032478259485180">"لغزاندن به بالا برای <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"لغزاندن به چپ برای <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"بدون قطع شدن، شامل هشدارها"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"بدون وقفه"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"فقط وقفههای اولویتدار"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"هشدار بعدی شما در ساعت <xliff:g id="ALARM_TIME">%s</xliff:g> است"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"هشدار بعدی شما <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g> است"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"هشدارتان را در ساعت <xliff:g id="ALARM_TIME">%s</xliff:g> نخواهید شنید"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"اعلانهای کمتر فوری در زیر"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"برای باز کردن دوباره ضربه بزنید"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"در حال شارژ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> تا شارژ کامل)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"مهمان"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ مهمان"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"خروج مهمان"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"برای یک دقیقه"</item>
<item quantity="other" msgid="6924190729213550991">"برای %d دقیقه"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"باز کردن تنظیمات ذخیره کننده باتری"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>٪٪"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"محتواها پنهان هستند"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> شروع به ضبط هر چیزی میکند که در صفحهنمایش شما نمایش داده میشود."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"دوباره نشان داده نشود"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"اکنون شروع شود"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 0de52c5..00431fa 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Kuvakaappaus tallennettu"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Katso kuvakaappaus koskettamalla."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Kuvakaappausta ei voitu tallentaa"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Kuvakaappauksen tallennus epäonnistui. Ulkoinen tallennustila voi olla käytössä."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB-tiedostonsiirtoasetukset"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Käytä mediasoittimena (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Käytä kamerana (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Sulje paneeli"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Lisää aikaa"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Vähennä aikaa"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G–3G-tiedonsiirto ei ole käytössä"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G-tiedonsiirto ei ole käytössä"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Mobiilitiedonsiirto ei ole käytössä"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Tiedonsiirto ei ole käytössä"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Laitteesi poisti tiedonsiirron käytöstä, koska se saavutti asettamasi rajan.\n\nOperaattorisi saattaa veloittaa sinulta lisämaksuja, jos otat sen uudelleen käyttöön."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Ota tiedonsiirto käyttöön"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Ei internetyhteyttä"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi yhdistetty"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Haetaan GPS-yhteyttä"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Haku"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Liu\'uta ylös ja <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Liu\'uta vasemmalle ja <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Ei keskeytyksiä tai hälytyksiä"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Ei häiriöitä"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Vain tärkeät häiriöt"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Seuraava hälytysaika on <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Seuraava hälytysaika on <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Et kuule hälytystä klo <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Vähemmän kiireelliset ilmoitukset ovat alla"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Avaa napauttamalla uudelleen"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Ladataan (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kunnes täynnä)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Vieras"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Vieras"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Kirjaa vieras ulos"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Minuutiksi"</item>
<item quantity="other" msgid="6924190729213550991">"%d minuutiksi"</item>
@@ -293,12 +297,7 @@
<!-- no translation found for battery_level_template (1609636980292580020) -->
<skip />
<string name="notification_hidden_text" msgid="1135169301897151909">"Sisältö piilotettu"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> alkaa tallentaa kaiken näytölläsi näkyvän."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Älä näytä uudelleen"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Aloita nyt"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index d92f0fb..085d0b3 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Capture d\'écran réussie"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Appuyez pour afficher votre capture d\'écran."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Impossible de réaliser une capture d\'écran"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Impossible enregistrer capture d\'écran. Périphérique de stockage peut-être en cours d\'utilisation."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Options transfert fichiers USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Installer comme un lecteur multimédia (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Installer comme un appareil photo (PTP)"</string>
@@ -270,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours... (chargée à 100 % dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Invité"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"Ajouter un invité"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Déconnecter l\'invité"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Pendant une minute"</item>
<item quantity="other" msgid="6924190729213550991">"Pendant %d minutes"</item>
@@ -283,7 +297,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Ouvrir les paramètres d\'économie d\'énergie"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g> %%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contenus masqués"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Déconnecter l\'invité"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> commencer à enregistrer tout ce qui s\'affiche sur votre écran."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ne plus afficher"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Commencer maintenant"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 61acdd5..003168a 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Capture d\'écran réussie"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Appuyez pour afficher votre capture d\'écran."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Impossible de réaliser une capture d\'écran"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Impossible enregistrer capture d\'écran. Périphérique de stockage peut-être en cours d\'utilisation."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Options transfert fichiers USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Installer en tant que lecteur multimédia (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Installer en tant qu\'appareil photo (PTP)"</string>
@@ -270,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours… (chargé à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Invité"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"Ajouter un invité"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Quitter le mode Invité"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Pendant une minute"</item>
<item quantity="other" msgid="6924190729213550991">"Pendant %d minutes"</item>
@@ -283,7 +297,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Ouvrir les paramètres de l\'économiseur de batterie"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g> %%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contenus masqués"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Quitter le mode Invité"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> va commencer à capturer tous les contenus affichés à l\'écran."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ne plus afficher"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Commencer"</string>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index ca0df92..7e3b441 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de pantalla gardada."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Toca para ver a captura de pantalla."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Non se puido facer a captura de pantalla."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Non se puido gardar a captura de pantalla. É posible que o almacenamento estea en uso."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Opcións de transferencia USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Inserir como reprodutor multimedia (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Inserir como cámara (PTP)"</string>
@@ -270,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Cargando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para finalizar a carga)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Convidado"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Convidado"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Retirar invitado"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Durante un minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Durante %d minutos"</item>
@@ -283,7 +297,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Abrir a configuración do aforrador de batería"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contido oculto"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Retirar invitado"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> comezará a capturar todo o que apareza na túa pantalla."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Non mostrar outra vez"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Iniciar agora"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 86877c7..b1ac437 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"स्क्रीनशॉट कैप्चर किया गया."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"अपना स्क्रीनशॉट देखने के लिए स्पर्श करें."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"स्क्रीनशॉट को कैप्चर नहीं किया जा सका."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"स्क्रीनशॉट को सहेजा नहीं जा सका. संभवत: संग्रहण उपयोग में है."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB फ़ाइल स्थानांतरण विकल्प"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"मीडिया प्लेयर के रूप में माउंट करें (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"कैमरे के रूप में माउंट करें (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"चार्ज हो रहा है (पूर्ण होने में <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> शेष)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"अतिथि"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ अतिथि"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"अतिथि मोड से बाहर निकलें"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"एक मिनट के लिए"</item>
<item quantity="other" msgid="6924190729213550991">"%d मिनट के लिए"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"बैटरी सेवर सेटिंग चालू करें"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"छिपी हुई सामग्री"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"अतिथि मोड से बाहर निकलें"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> आपके स्क्रीन पर प्रदर्शित प्रत्येक सामग्री को कैप्चर करना प्रारंभ कर देगी."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"फिर से न दिखाएं"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"अब प्रारंभ करें"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index da0f6dd..6e34b35 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Zaslon je snimljen."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Dodirnite za prikaz snimke zaslona."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Nije bilo moguće snimiti zaslon."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Nije bilo moguće spremiti snimku zaslona. Možda se upotrebljava pohrana."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Opcije USB prijenosa datoteka"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Učitaj kao media player (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Učitaj kao fotoaparat (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Zatvori ploču"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Više vremena"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Manje vremena"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G-3G podaci isključeni"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G podaci isključeni"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Mobilni podaci isključeni"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Podaci su isključeni"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Uređaj je isključio podatke jer je dosegnuo ograničenje koje ste postavili.\n\nAko ih ponovno uključite, mogući su dodatni troškovi za mobilne usluge."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Uključi podatke"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nema internetske veze"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi povezan"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Traženje GPS-a"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Pretraživanje"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Kliznite prema gore za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Kliznite lijevo za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Bez prekida, uključujući alarme"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Bez prekida"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Samo prioritetni prekidi"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Vaš sljedeći alarm bit će u <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Vaš sljedeći alarm bit će u <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Nećete čuti alarm u <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Manje hitne obavijesti pri dnu"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Dodirnite opet za otvaranje"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Punjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napunjenosti)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Gost"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ gost"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Izlaz iz gostujućeg načina"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Jednu minutu"</item>
<item quantity="other" msgid="6924190729213550991">"%d min"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Otvaranje postavki štednje baterije"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Sadržaj je skriven"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"Aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> počet će snimati sve što se prikazuje na zaslonu."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Ne prikazuj ponovo"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Započni sad"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index e9f6c29..f3e8025 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Képernyőkép rögzítve."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Megérintésével megtekintheti a képernyőképet."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Nem sikerült rögzíteni a képernyőképet."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Nem lehet menteni a képernyőképet. Lehet, hogy a tároló használatban van."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB-fájlátvitel beállításai"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Csatlakoztatás médialejátszóként (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Csatlakoztatás kameraként (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Töltés (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> a teljes töltöttségig)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Vendég"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ vendég"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Vendég kiléptetése"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Egy percen át"</item>
<item quantity="other" msgid="6924190729213550991">"%d percen át"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Akkumulátorkímélő mód beállításainak megnyitása"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>. szint"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Tartalomjegyzék elrejtve"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Vendég kiléptetése"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"A(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> alkalmazás rögzíteni fog mindent, ami megjelenik a képernyőn."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ne jelenjen meg többé"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Indítás most"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 0ff2136..813b253 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Էկրանի հանույթը լուսանկարվել է:"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Հպեք ձեր էկրանի հանույթը տեսնելու համար:"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Չհաջողվեց լուսանկարել էկրանի հանույթը:"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Չհաջողվեց պահել էկրանի հանույթը: Հնարավոր է` պահոցն օգտագործման մեջ է:"</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB ֆայլերի փոխանցման ընտրանքներ"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Միացնել որպես մեդիա նվագարկիչ (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Միացնել որպես ֆոտոխցիկ (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Փակել վահանակը"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Ավելացնել ժամանակը"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Քչացնել ժամանակը"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G-3G տվյալների կապն անջատված է"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G տվյալների կապն անջատված է"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Բջջային տվյալներն անջատված են"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Տվյալների կապն անջատված է"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Տվյալների կապը ձեր սարքում անջատվեց, քանի որ դուք հատել եք նշված սահմանաչափը:\n\nԱյն հետ միացնելուց հետո հնարավոր են հավելյալ վճարներ ձեր օպերատորից:"</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Միացնել տվյալների կապը"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Ինտերնետ կապ չկա"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi-ը միացված է"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Որոնում է GPS"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Որոնել"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Սահեցրեք վերև <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>-ի համար:"</string>
<string name="description_direction_left" msgid="7207478719805562165">"Սահեցրեք ձախ` <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>-ի համար:"</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Առանց ընդհատումների՝ ներառյալ զարթուցիչները"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Առանց ընդհատումների"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Միայն կարևոր ընդհատումներ"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Ձեր հաջորդ զարթուցիչի ժամն է՝ <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Ձեր հաջորդ զարթուցիչի օրն է՝ <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Դուք չեք լսի ձեր զարթուցիչը <xliff:g id="ALARM_TIME">%s</xliff:g>-ին:"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Պակաս հրատապ ծանուցումները ստորև"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Կրկին հպեք՝ բացելու համար"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> մինչև լրիվ լիցքավորումը)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Հյուր"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Հյուր"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Դուրս գալ հյուրի ռեժիմից"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Մեկ րոպե"</item>
<item quantity="other" msgid="6924190729213550991">"%d րոպե"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Բացել մարտկոցի տնտեսման կարգավորումները"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Բովանդակությունը թաքցված է"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ծրագիրը կսկսի հավաքագրել այն ամենն ինչ ցուցադրվում է ձեր էկրանին:"</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Այլևս ցույց չտալ"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Մեկնարկել հիմա"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index dbcc71d..80df525 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Tangkapan layar diambil."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Sentuh untuk melihat tangkapan layar Anda."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Tidak dapat mengambil tangkapan layar."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Tidak dapat menyimpan tangkapan layar. Penyimpanan mungkin sedang digunakan."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Opsi transfer file USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Pasang sebagai pemutar media (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Pasang sebagai kamera (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Tutup panel"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Lebih lama"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Lebih cepat"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"Data 2G-3G nonaktif"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"Data 4G nonaktif"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Data seluler nonaktif"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Data nonaktif"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Perangkat Anda menonaktifkan data karena data mencapai batas yang Anda setel.\n\nMengaktifkan kembali data dapat membuat Anda terkena biaya dari operator."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Aktifkan data"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Tidak ada sambungan internet"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi tersambung"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Menelusuri GPS"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Telusuri"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Geser ke atas untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Geser ke kiri untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Tanpa gangguan, termasuk alarm"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Tidak ada interupsi"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Hanya interupsi prioritas"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Alarm Anda berikutnya pukul <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Alarm Anda berikutnya hari <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Anda tidak akan mendengar alarm pukul <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Pemberitahuan kurang darurat di bawah"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Ketuk lagi untuk membuka"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Mengisi daya (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hingga penuh)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Tamu"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Tamu"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Keluar dari tamu"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Selama satu menit"</item>
<item quantity="other" msgid="6924190729213550991">"Selama %d menit"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Buka setelan penghemat baterai"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Konten tersembunyi"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> akan mulai menangkap apa saja yang ditampilkan pada layar Anda."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Jangan tampilkan lagi"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Mulai sekarang"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 7378dff..adae78d 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Skjámynd var tekin."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Snertu til að skoða skjámyndina."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Ekki tókst að taka skjámynd."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Ekki tókst að vista skjámynd. Geymslan kann að vera í notkun."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Valkostir USB-skráaflutnings"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Tengja sem efnisspilara (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Tengja sem myndavél (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Í hleðslu (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> fram að fullri hleðslu)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Gestur"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Gestur"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Loka gestastillingu"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Í eina mínútu"</item>
<item quantity="other" msgid="6924190729213550991">"Í %d mínútur"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Opna stillingar rafhlöðusparnaðar"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Innihald falið"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Loka gestastillingu"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> mun fanga allt sem birtist á skjánum."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ekki sýna þetta aftur"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Byrja núna"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index f813cdf..8c87056 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot acquisito."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Tocca per visualizzare il tuo screenshot."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Impossibile acquisire lo screenshot."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Impossibile salvare lo screenshot. L\'archivio esterno potrebbe essere in uso."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Opzioni trasferimento file USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Monta come lettore multimediale (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Monta come videocamera (PTP)"</string>
@@ -270,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"In carica (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> al termine)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Ospite"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ ospite"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Esci dalla modalità ospite"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Per un minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Per %d minuti"</item>
@@ -283,7 +297,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Apri impostazioni risparmio batteria"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contenuti nascosti"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Esci dalla modalità ospite"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> inizierà ad acquisire tutto ciò che è visualizzato sul tuo schermo."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Non·mostrare·più"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Avvia adesso"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index d0151bb..f5d67f3 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"צילום המסך בוצע."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"גע כדי להציג את צילום המסך שלך"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"לא ניתן לבצע צילום מסך."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"לא ניתן לשמור את צילום המסך. ייתכן שנעשה שימוש באמצעי אחסון."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"אפשרויות העברת קבצים ב-USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"טען כנגן מדיה (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"טען כמצלמה (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"טוען (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> עד לסיום)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"אורח"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ אורח"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"צא ממצב אורח"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"למשך דקה אחת"</item>
<item quantity="other" msgid="6924190729213550991">"למשך %d דקות"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"פתח את ההגדרות של \'חיסכון בסוללה\'"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"התוכן מוסתר"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"צא ממצב אורח"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> יתחיל להקליט את כל התוכן המוצג במסך שלך."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"אל תציג שוב"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"התחל כעת"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 856eccf..060a565 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"スクリーンショットを取得しました。"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"タップしてスクリーンショットを表示します。"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"スクリーンショットをキャプチャできませんでした。"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"スクリーンショットを保存できませんでした。ストレージが使用中の可能性があります。"</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USBファイル転送オプション"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"メディアプレーヤー(MTP)としてマウント"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"カメラ(PTP)としてマウント"</string>
@@ -168,18 +169,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"パネルを閉じる"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"長くする"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"短くする"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G~3Gデータが無効になりました"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4Gデータが無効になりました"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"モバイルデータが無効になりました"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"データが無効になりました"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"設定された上限に達したため、端末のデータ接続が無効になりました。\n\n有効に戻すと、携帯通信会社から課金される可能性があります。"</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"データ接続を有効にする"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"インターネット未接続"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi接続済み"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"GPSで検索中"</string>
@@ -257,16 +252,12 @@
<string name="description_target_search" msgid="3091587249776033139">"検索します"</string>
<string name="description_direction_up" msgid="7169032478259485180">"上にスライドして<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>を行います。"</string>
<string name="description_direction_left" msgid="7207478719805562165">"左にスライドして<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>を行います。"</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"サイレント(アラームなど)"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"サイレント"</string>
- <string name="zen_important_interruptions" msgid="3477041776609757628">"優先的な中断のみ"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_important_interruptions" msgid="3477041776609757628">"重要な通知のみ"</string>
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"次のアラームは<xliff:g id="ALARM_TIME">%s</xliff:g>です"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"次のアラームは<xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>です"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"<xliff:g id="ALARM_TIME">%s</xliff:g>のアラームは鳴りません"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"緊急度の低い通知を下に表示"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"開くにはもう一度タップしてください"</string>
@@ -280,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中(フル充電まで<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"ゲスト"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ ゲスト"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"ゲストを終了"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1分"</item>
<item quantity="other" msgid="6924190729213550991">"%d分"</item>
@@ -293,12 +297,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"バッテリーセーバーの設定を開く"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"コンテンツが非表示"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>で、画面に表示されているコンテンツのキャプチャを開始します。"</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"次回から表示しない"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"今すぐ開始"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index cccb7a8..a99730c 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"სკრინშოტი გადაღებულია."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"შეეხეთ ეკრანის სურათის სანახავად."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ვერ მოხერხდა ეკრანის ანაბეჭდის გადაღება."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"ეკრანის სურათი ვერ შეინახა. შესაძლოა, მეხსიერება უკვე დაკავებულია."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB ფაილის ტრანსფერის პარამეტრები"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"მედია-საკრავად (MTP) ჩართვა"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"მიუერთეთ როგორც კამერა (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"არეს დახურვა"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"მეტი დრო"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"ნაკლები დრო"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G-3G მონაც. გადაცემა გამორთულია"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G მონაც. გადაცემა გამორთულია"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"ფიჭური ინტერნეტი გამორთულია"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"მონაცემთა გადაცემა გამორთულია"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"თქვენმა მოწყობილობამ მონაცემები გამორთო, რადგან თქვენ მიერ დაყენებულ ლიმიტს მიაღწია.\n\nმისი კვლავ გააქტიურებით შესაძლოა დაგეკისროთ გადასახადი თქვენი ოპერატორისგან."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"მონაცემთა ჩართვა"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"ინტერნეტ კავშირი არ არის"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi დაკავშირებულია"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"GPS-ის ძებნა"</string>
@@ -218,7 +213,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"არ არის დაკავშირებული."</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ქსელი არ არის"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi გამორთულია"</string>
- <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"შენახუი ქსელები მიუწვდომელია"</string>
+ <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"შენახული ქსელები მიუწვდომელია"</string>
<string name="quick_settings_cast_title" msgid="1893629685050355115">"ეკრანის გადაცემა"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"გადაიცემა"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"უსახელო მოწყობილობა"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"ძიება"</string>
<string name="description_direction_up" msgid="7169032478259485180">"გაასრიალეთ ზემოთ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>-თვის."</string>
<string name="description_direction_left" msgid="7207478719805562165">"გაასრიალეთ მარცხნივ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>-თვის."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"შეწყვეტების გარეშე, მაღვიძარების ჩათვლით"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"შეწყვეტების გარეშე"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"მხოლოდ პრიორიტეტული შეწყვეტები"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"თქვენი შემდეგი მაღვიძარაა <xliff:g id="ALARM_TIME">%s</xliff:g>-ზე"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"თქვენი შემდეგი მაღვიძარაა <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"თქვენს მაღვიძარას <xliff:g id="ALARM_TIME">%s</xliff:g>-ზე ვერ გაიგონებთ"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"ქვემოთ მითითებულია ნაკლებად სასწრაფო შეტყობინებები"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"შეეხეთ ისევ გასახსნელად"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>-ის შეცვლა დასრულებამდე)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"სტუმარი"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ სტუმარი"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"სტუმრის გასვლა"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"ერთი წუთით"</item>
<item quantity="other" msgid="6924190729213550991">"%d წუთით"</item>
@@ -290,14 +294,8 @@
<string name="battery_saver_notification_text" msgid="7796554871101546872">"მოწყობილობის წარმადობა შემცირებულია."</string>
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"ბატარეის დამზოგის პარამეტრების გახსნა"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
- <!-- no translation found for notification_hidden_text (1135169301897151909) -->
- <skip />
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="notification_hidden_text" msgid="1135169301897151909">"შიგთავსი დამალულია"</string>
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> დაიწყებს იმ ყველაფრის აღბეჭდვას, რაც თქვენს ეკრანზე ჩანს."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"აღარ მაჩვენო"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"დაწყება ახლავე"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index a3505a3..de114d2 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Скриншот сақталды."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Скриншотты көру үшін түрту."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Скриншот жасалмады."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Скриншотты сақтай алмады. Жад қолданыста болуы мүмкін."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB файлын жіберу опциялары"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Медиа ойнатқыш (MTP) ретінде қосыңыз"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Камера ретінде (PTP) қосыңыз"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Тақтаны жабу"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Көбірек уақыт"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Азырақ уақыт"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G-3G деректері өшірулі"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G деректері өшірулі"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Ұялы деректер өшірулі"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Деректер өшірулі"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Құрылғыңыз сіз орнатқан шекке жеткендіктен деректерді өшірді.\n\nОны қайтадан қосу оператордың ақылар алуына әкелуі мүмкін."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Деректерді қосу"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Интернет байланысы жоқ"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi қосулы"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"GPS қызметін іздеуде"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Іздеу"</string>
<string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> үшін жоғары сырғыту."</string>
<string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> үшін солға сырғыту."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Үзілістерсіз, соның ішінде, дабылдарсыз"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Үзулерсіз"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Тек басым үзулер"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Келесі дабыл — <xliff:g id="ALARM_TIME">%s</xliff:g> уақытында"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Келесі дабыл — <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"<xliff:g id="ALARM_TIME">%s</xliff:g> уақытында дабылды естімейсіз"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Шұғылдығы азырақ хабарландырулар төменде"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Ашу үшін қайта түртіңіз"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Зарядталуда (толғанша <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Қонақ"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Қонақ"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Қонақтан шығу"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Бір минут бойы"</item>
<item quantity="other" msgid="6924190729213550991">"%d минут бойы"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Батарея үнемдегіш параметрлерін ашу"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Мазмұн жасырылған"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> экранда көрсетілгеннің барлығын түсіре бастайды."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Қайта көрсетпеу"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Қазір бастау"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 90f66ae..0ac6f40 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"បានចាប់យករូបថតអេក្រង់។"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"ប៉ះ ដើម្បីមើលរូបថតអេក្រង់របស់អ្នក។"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"មិនអាចចាប់យករូបថតអេក្រង់។"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"មិនអាចរក្សាទុករូបថតអេក្រង់។ ឧបករណ៍ផ្ទុកអាចកំពុងប្រើ។"</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"ជម្រើសផ្ទេរឯកសារតាមយូអេសប៊ី"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"ភ្ជាប់ជាកម្មវិធីចាក់មេឌៀ (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"ភ្ជាប់ជាម៉ាស៊ីនថត (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"បិទបន្ទះ"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"ពេលច្រើនជាង"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"ពេលតិចជាង"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"ទិន្នន័យ 2G-3G បានបិទ"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"ទិន្នន័យ 4G បានបិទ"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"ទិន្នន័យចល័តបានបិទ"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"ទិន្នន័យបានបិទ"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"ឧបករណ៍របស់អ្នកបានបិទទិន្នន័យ ព្រោះវាបានដល់កម្រិតដែលអ្នកបានកំណត់។\n\nបើកវាឡើងវិញអាចគិតលុយពីក្រុមហ៊ុនបញ្ជូនរបស់អ្នក។"</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"បើកទិន្នន័យ"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"គ្មានការតភ្ជាប់អ៊ីនធឺណិត"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"បានភ្ជាប់វ៉ាយហ្វាយ"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"ស្វែងរក GPS"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"ស្វែងរក"</string>
<string name="description_direction_up" msgid="7169032478259485180">"រុញឡើងលើដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
<string name="description_direction_left" msgid="7207478719805562165">"រុញទៅឆ្វេងដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"មិនមានការផ្អាក រួមទាំងការជូនដំណឹង"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"គ្មានការផ្អាក"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"តែការផ្អាកអាទិភាពប៉ុណ្ណោះ"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"ការជូនដំណឹងបន្ទាប់របស់អ្នកគឺនៅម៉ោង <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"ការជូនដំណឹងបន្ទាប់របស់អ្នកគឺនៅ <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"អ្នកនឹងមិនឮការជូនដំណឹងរបស់អ្នកនៅម៉ោង <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"ការជូនដំណឹងមិនសូវបន្ទាន់ខាងក្រោម"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"ប៉ះម្ដងទៀត ដើម្បីបើក"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"កំពុងបញ្ចូលថ្ម (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ទើបពេញ)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"ភ្ញៀវ"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ ភ្ញៀវ"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"ភ្ញៀវចាកចេញ"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"សម្រាប់មួយនាទី"</item>
<item quantity="other" msgid="6924190729213550991">"សម្រាប់ %d នាទី"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"បើកការកំណត់កម្មវិធីសន្សំថ្ម"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"បានលាក់មាតិកា"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> នឹងចាប់ផ្ដើមចាប់យកអ្វីៗគ្រប់យ៉ាងដែលបង្ហាញលើអេក្រង់របស់អ្នក។"</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"កុំបង្ហាញម្ដងទៀត"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"ចាប់ផ្ដើមឥឡូវ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 2c58be6..d89687e 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಸೆರೆಹಿಡಿಯಲಾಗಿದೆ."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ಶಾಟ್ ವೀಕ್ಷಿಸಲು ಸ್ಪರ್ಶಿಸಿ."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಸೆರೆಹಿಡಿಯಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ಸಂಗ್ರಹಣೆಯು ಬಳಕೆಯಲ್ಲಿರಬಹುದು."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB ಫೈಲ್ ವರ್ಗಾವಣೆ ಆಯ್ಕೆಗಳು"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"ಮೀಡಿಯಾ ಪ್ಲೇಯರ್ ರೂಪದಲ್ಲಿ ಅಳವಡಿಸಿ (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"ಕ್ಯಾಮರಾ ರೂಪದಲ್ಲಿ ಅಳವಡಿಸಿ (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ ( ಪೂರ್ತಿ ಆಗುವವರೆಗೆ <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"ಅತಿಥಿ"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ ಅತಿಥಿ"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"ಅತಿಥಿ ಅನ್ನು ನಿರ್ಗಮಿಸಿ"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"ಒಂದು ನಿಮಿಷದವರೆಗೆ"</item>
<item quantity="other" msgid="6924190729213550991">"%d ನಿಮಿಷಗಳವರೆಗೆ"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"ಬ್ಯಾಟರಿ ರಕ್ಷಕದ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಿರಿ"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"ವಿಷಯಗಳನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"ಅತಿಥಿ ಅನ್ನು ನಿರ್ಗಮಿಸಿ"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"ನಿಮ್ಮ ಪರದೆಯ ಮೇಲೆ ಪ್ರದರ್ಶಿಸಲಾಗುವ ಎಲ್ಲವನ್ನೂ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಯು ಸೆರೆಹಿಡಿಯಲು ಪ್ರಾರಂಭಿಸುತ್ತದೆ."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"ಮತ್ತೊಮ್ಮೆ ತೋರಿಸದಿರಿ"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"ಈಗ ಪ್ರಾರಂಭಿಸಿ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index cd2c580..f2488f6 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -75,7 +75,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"캡쳐화면 저장됨"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"캡쳐화면을 보려면 터치하세요."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"캡쳐화면을 캡쳐하지 못했습니다."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"캡쳐화면을 저장할 수 없습니다. 저장소를 사용 중인 것 같습니다."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB 파일 전송 옵션"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"미디어 플레이어로 마운트(MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"카메라로 마운트(PTP)"</string>
@@ -168,18 +169,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"패널 닫기"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"시간 늘리기"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"시간 줄이기"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G-3G 데이터 사용 중지됨"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G 데이터 사용 중지됨"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"이동통신 데이터 사용 중지됨"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"데이터 사용 중지됨"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"데이터가 설정 한도에 도달하여 사용 중지되었습니다.\n\n데이터를 다시 사용 설정하면 이동통신사로부터 대금이 청구될 수 있습니다."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"데이터 사용 설정"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"인터넷에 연결되지 않음"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi 연결됨"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"GPS 검색 중"</string>
@@ -257,16 +252,12 @@
<string name="description_target_search" msgid="3091587249776033139">"검색"</string>
<string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>하려면 위로 슬라이드"</string>
<string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>하려면 왼쪽으로 슬라이드"</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"방해 금지(알람 포함)"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"모든 알림 차단"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"최우선 알림만 수신"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"다음 알람 시각: <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"다음 알람 일시: <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"<xliff:g id="ALARM_TIME">%s</xliff:g>에 알람을 들을 수 없습니다."</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"아래에 덜 급한 알림 표시"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"다시 탭하여 열기"</string>
@@ -280,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"충전 중(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> 후 충전 완료)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"손님"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"새 손님 추가"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"손님 모드 종료"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1분 동안"</item>
<item quantity="other" msgid="6924190729213550991">"%d분 동안"</item>
@@ -293,12 +297,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"배터리 세이버 설정 열기"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"숨겨진 콘텐츠"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에서 화면에 표시된 모든 것을 캡처하기 시작합니다."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"다시 표시 안함"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"시작하기"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 9f6892c..0d6867c 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -96,7 +96,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Скриншот тартылды."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Тийип, скриншотту көрүңүз."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Скриншот кылынбай жатат."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Скриншот сакталбай жатат. Сактагыч пайдаланууда болушу мүмкүн."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<!-- no translation found for usb_preference_title (6551050377388882787) -->
<skip />
<!-- no translation found for use_mtp_button_title (4333504413563023626) -->
@@ -192,18 +193,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Тактаны жабуу"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Көбүрөөк убакыт"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Азыраак убакыт"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2Гб-3Гб көлөмдөгү дайындар өчүрүлдү."</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4Гб көлөмдөгү дайындар өчүрүлдү"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Уюктук дайындар тармагы өчүк"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Дайындарды кабыл алуу өчүрүлгөн."</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Белгиленген эң жогорку чекке жеткендиктен, түзмөгүңүз дайындарды кабыл алууну токтотту.\n\nДайындарды кабыл алууну улантам десеңиз, операторго акы төлөп калышыңыз мүмкүн."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Дайындарды алууну иштетүү"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Интернет байланыш жок"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi байланышта"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"GPS издөө"</string>
@@ -281,16 +276,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Издөө"</string>
<string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> үчүн жогору жылмыштырыңыз."</string>
<string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> үчүн солго жылмыштырыңыз."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Үзгүлтүктөр, ошондой эле үн ишараттары болбойт"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Үзгүлтүксүз"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Артыкчылыктуу үзгүлтүктөр гана"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Кийинки үн ишараты саат <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Кийинки үн ишараты <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Саат <xliff:g id="ALARM_TIME">%s</xliff:g> үн ишаратын укпайсыз."</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Анчейин шашылыш эмес эскертмелер төмөндө"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Ачуу үчүн кайра таптап коюңуз"</string>
@@ -304,6 +295,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Кубатталууда (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> толгонго чейин)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Конок"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Конок"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Конок режиминен чыгуу"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Бир мүнөткө"</item>
<item quantity="other" msgid="6924190729213550991">"%d мүнөткө"</item>
@@ -317,12 +321,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Батареяны үнөмдөгүчтүн жөндөөлөрүн ачуу"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Мазмундар жашырылган"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> экранга чыккан нерсенин баарын сүрөткө тарта баштайт."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Экинчи көрсөтүлбөсүн"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Азыр баштоо"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index ef98cca..83ace48 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"ຖ່າຍຮູບໜ້າຈໍແລ້ວ"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"ແຕະເພື່ອເບິ່ງພາບໜ້າຈໍຂອງທ່ານ."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ບໍ່ສາມາດຖ່າຍຮູບໜ້າຈໍໄດ້"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"ບໍ່ສາມາດບັນທຶກພາບໜ້າຈໍໄດ້. ບ່ອນຈັດເກັບອາດກຳລັງຖືກນຳໃຊ້ຢູ່."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB ໂຕເລືອກການຍ້າຍໄຟລ໌"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"ເຊື່ອມຕໍ່ເປັນ media player (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"ເຊື່ອມຕໍ່ເປັນກ້ອງຖ່າຍຮູບ (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ກຳລັງສາກໄຟ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ກວ່າຈະເຕັມ)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"ແຂກ"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ ແຂກ"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"ອອກຈາກຜູ່ມາຢາມ"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"ເປັນເວລານຶ່ງນາທີ"</item>
<item quantity="other" msgid="6924190729213550991">"ເປັນເວລາ %d ນາທີ"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"ເປີດການຕັ້ງຄ່າໂຕປະຢັດແບັດເຕີຣີ"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"ເນື້ອຫາຖືກເຊື່ອງແລ້ວ"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"ອອກຈາກຜູ່ມາຢາມ"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ຈະເລີ່ມບັນທຶກທຸກຢ່າງທີ່ສະແດງຜົນໃນໜ້າຈໍທ່ານ."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"ບໍ່ຕ້ອງສະແດງອີກ"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"ເລີ່ມດຽວນີ້"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index f0ffafe..567aa00 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Ekrano kopija užfiksuota."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Palieskite, kad peržiūrėtumėte ekrano kopiją."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Nepavyko užfiksuoti ekrano kopijos."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Nepavyko išsaugoti ekrano kopijos. Gali būti naudojama atmintis."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB failo perdavimo parinktys"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Įmontuoti kaip medijos leistuvę (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Įmontuoti kaip fotoaparatą (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Uždaryti skydelį"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Daugiau laiko"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mažiau laiko"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G–3G duomenys išjungti"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G duomenys išjungti"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Mobiliojo ryšio duomenys išjungti"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Duomenys išjungti"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Įrenginys išjungė duomenis, nes buvo pasiektas nustatytas limitas.\n\nVėl juos įjungus gali būti taikomi operatoriaus mokesčiai."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Įjungti duomenis"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nėra interneto ryš."</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Prisij. prie „Wi-Fi“"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Ieškoma GPS"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Paieška"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Slyskite aukštyn link <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Slyskite į kairę link <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Jokių pertraukčių, įskaitant signalus"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Jokių pertraukčių"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Tik prioritetinės pertrauktys"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Kito signalo laikas: <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Kito signalo laikas: <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"<xliff:g id="ALARM_TIME">%s</xliff:g> signalo negirdėsite"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Mažiau skubūs pranešimai toliau"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Palieskite dar kartą, kad atidarytumėte"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Kraunama (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> iki visiško įkrovimo)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Svečias"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Svečias"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Išeiti iš svečio režimo"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1 min."</item>
<item quantity="other" msgid="6924190729213550991">"%d min."</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Atidaryti akumuliatoriaus tausojimo priemonės nustatymus"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g> %%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Turinys paslėptas"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"„<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ pradės fiksuoti viską, kas rodoma jūsų ekrane."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Daugiau neberodyti"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Pradėti dabar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 6c5cfe4..3eb35aa 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Ekrānuzņēmums ir uzņemts."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Pieskarieties, lai skatītu ekrānuzņēmumu."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Nevarēja uzņemt ekrānuzņēmumu."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Nevarēja saglabāt ekrānuzņēmumu. Iespējams, tiek izmantota atmiņa."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB failu pārsūtīšanas opcijas"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Pievienot kā multivides atskaņotāju (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Pievienot kā kameru (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Aizvērt paneli"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Ilgāk"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mazāks laiks"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G–3G dati ir atslēgti"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G dati ir atslēgti"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Mobilie dati ir atslēgti"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Dati ir atslēgti"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Jūsu ierīcē tika atslēgta datu lietošana, jo tika sasniegts jūsu noteiktais ierobežojums.\n\nJa atkal ieslēgsiet datu lietošanu, iespējams, jūsu mobilo sakaru operators iekasēs maksu."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Ieslēgt datus"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nav interneta sav."</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Izv. sav. ar Wi-Fi"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Notiek GPS meklēšana..."</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Meklēt"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Velciet uz augšu, lai veiktu šādu darbību: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Velciet pa kreisi, lai veiktu šādu darbību: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Bez pārtraukumiem, tostarp bez signāliem"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Nepārtraukt"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Tikai prioritārie pārtraukumi"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Jūsu nākamā signāla laiks: <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Jūsu nākamā signāla datums un laiks: <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Jūs nedzirdēsiet iestatīto signālu: <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Mazāk steidzami paziņojumi tiek rādīti tālāk"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Pieskarieties vēlreiz, lai atvērtu"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Notiek uzlāde (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> līdz pilnīgai uzlādei)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Viesis"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+Viesis"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Iziet no viesa režīma"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Vienu minūti"</item>
<item quantity="other" msgid="6924190729213550991">"%d min"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Atvērt akumulatora enerģijas taupīšanas režīma iestatījumus"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Saturs paslēpts"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sāks uzņemt visu, kas tiks rādīts jūsu ekrānā."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Vairs nerādīt"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Sākt tūlīt"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index b8f6ce5..39021ca 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Сликата на екранот е снимена."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Допрете за да ја видите сликата на екранот."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Сликата на екранот не можеше да се сними."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Сликата на екранот не можеше да се зачува. Можеби меморијата е во употреба."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Пренос на датотека со УСБ"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Монтирај како мултимедијален плеер (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Монтирај како фотоапарат (PTP)"</string>
@@ -168,18 +169,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Затвори ја плочата"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Повеќе време"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Помалку време"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"Податоците 2G-3G се исклучени"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"Податоците 4G се исклучени"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Мобилните податоци се исклучени"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Податоците се исклучени"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Вашиот уред ги исклучи податоците затоа што го достигнаа лимитот што го поставивте.\n\nСо повторно вклучување, операторот може да ви наплати за тоа."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Вклучи податоци"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Нема интернет"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Поврзано на Wi-Fi"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Се пребарува за ГПС"</string>
@@ -257,16 +252,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Пребарај"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Лизгај нагоре за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Лизгај налево за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Без прекини, вклучувајќи аларми"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Без прекини"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Само приоритетни прекини"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Следниот аларм е во <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Следниот аларм е <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Нема да го слушнете алармот во <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Долу се помалку итни известувања"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Допрете повторно за да се отвори"</string>
@@ -280,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Се полни (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> додека не се наполни)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Гостин"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ гостин"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Излези како гостин"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"За една минута"</item>
<item quantity="other" msgid="6924190729213550991">"За %d минути"</item>
@@ -293,12 +297,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Отвори ги поставките за штедачот на батерија"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Содржините се скриени"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ќе започне да презема сѐ што се прикажува на вашиот екран."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Не покажувај повторно"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Започни сега"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 663aa35..61f949b 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"സ്ക്രീൻഷോട്ട് എടുത്തു."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"നിങ്ങളുടെ സ്ക്രീൻഷോട്ട് കാണാനായി സ്പർശിക്കുക."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"സ്ക്രീൻഷോട്ട് എടുക്കാൻ കഴിഞ്ഞില്ല."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കാൻ കഴിഞ്ഞില്ല. സംഭരണം ഉപയോഗത്തിലായിരിക്കാം."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB ഫയൽ കൈമാറൽ ഓപ്ഷനുകൾ"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"ഒരു മീഡിയ പ്ലേയറായി (MTP) മൗണ്ടുചെയ്യുക"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"ഒരു ക്യാമറയായി (PTP) മൗണ്ടുചെയ്യുക"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ചാർജ്ജുചെയ്യുന്നു (പൂർണ്ണമാകുന്നതിന്, <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"അതിഥി"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ അതിഥി"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"അതിഥി മോഡിൽ നിന്ന് പുറത്തുകടക്കുക"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"ഒരു മിനിറ്റ് ദൈർഘ്യം"</item>
<item quantity="other" msgid="6924190729213550991">"%d മിനിറ്റ് ദൈർഘ്യം"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"ബാറ്ററി സേവർ ക്രമീകരണങ്ങൾ തുറക്കുക"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"കോൺടാക്റ്റുകൾ മറച്ചു"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"അതിഥി മോഡിൽ നിന്ന് പുറത്തുകടക്കുക"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"നിങ്ങളുടെ സ്ക്രീനിൽ പ്രദർശിപ്പിച്ചിരിക്കുന്ന എല്ലാ കാര്യങ്ങളും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ക്യാപ്ചർ ചെയ്യുന്നത് ആരംഭിക്കും."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"വീണ്ടും കാണിക്കരുത്"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"ഇപ്പോൾ ആരംഭിക്കുക"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index e4d3222..2149df3 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Дэлгэцийн агшинг авсан."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Дэлгэцийн агшныг харах бол хүрнэ үү."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Дэлгэцийн агшинг авч чадсангүй."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Дэлгэцийн агшинг хадгалж чадсангүй. Сан ашиглагдаж байгаа бололтой."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB файл шилжүүлэх сонголт"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Медиа тоглуулагч(MTP) болгон залгах"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Камер болгон(PTP) залгах"</string>
@@ -268,6 +269,13 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Цэнэглэж байна (дүүргэхэд <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Зочин"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Зочин"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Зочноос гарах"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="7587460301980067285">"Зочны нэвтрэлтээс гарч байна уу?"</string>
+ <string name="guest_exit_guest_dialog_message" msgid="10255285459589280">"Зочны нэвтрэлтээс гарснаар локал датаг арилгах болно."</string>
+ <string name="guest_wipe_session_title" msgid="6419439912885956132">"Тавтай морилно уу!"</string>
+ <string name="guest_wipe_session_message" msgid="5369763062345463297">"Та шинээр нэвтрэх гэж байна уу?"</string>
+ <string name="guest_wipe_session_wipe" msgid="9154291314115781448">"Тийм"</string>
+ <string name="guest_wipe_session_dontwipe" msgid="850084868661344050">"Үгүй, баярлалаа"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Нэг минутын турш"</item>
<item quantity="other" msgid="6924190729213550991">"%d минутын турш"</item>
@@ -281,7 +289,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Батерей хэмнэгчийн тохиргоог нээх"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Контентыг нуусан"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Зочноос гарах"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> таны дэлгэц дээр гаргасан бүх зүйлийн зургийг авч эхэлнэ."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Дахиж үл харуулах"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Одоо эхлүүлэх"</string>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index 36e2caa..4e6914d 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"स्क्रीनशॉट कॅप्चर केला."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"आपला स्क्रीनशॉट पाहण्यासाठी स्पर्श करा."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"स्क्रीनशॉट कॅप्चर करू शकलो नाही."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"स्क्रीनशॉट जतन करू शकलो नाही. संचयन वापरात असू शकते."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB फाईल स्थानांतरण पर्याय"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"मीडिया प्लेअर म्हणून माउंट करा (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"कॅमेरा म्हणून माउंट करा (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण होईपर्यंत) चार्ज होत आहे"</string>
<string name="guest_nickname" msgid="8059989128963789678">"अतिथी"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ अतिथी"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"निर्गमन करणारे अतिथी"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"एक मिनिटासाठी"</item>
<item quantity="other" msgid="6924190729213550991">"%d मिनिटांसाठी"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"बॅटरी बचतकर्ता सेटिंग्ज उघडा"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"लपविलेली सामग्री"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"निर्गमन करणारे अतिथी"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> आपल्या स्क्रीनवर प्रदर्शित होणारी प्रत्येक गोष्ट कॅप्चर करणे प्रारंभ करेल."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"पुन्हा दर्शवू नका"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"आता प्रारंभ करा"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 1e29567..3e5e3c3 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Tangkapan skrin ditangkap."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Sentuh untuk melihat tangkapan skrin anda."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Tidak dapat menangkap tangkapan skrin."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Tidak boleh menyimpan tangkapan skrin. Storan mungkin sedang digunakan."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Pilihan pemindahan fail USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Lekapkan sebagai pemain media (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Lekapkan sebagai kamera (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Tutup panel"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Lagi masa"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Kurang masa"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"Data 2G-3G dimatikan"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"Data 4G dimatikan"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Data selular dimatikan"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Data dimatikan"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Peranti anda mematikan data kerana telah mencapai had yang anda tetapkan.\n\nMenghidupkan data semula boleh menyebabkan anda dikenakan caj oleh pembawa anda."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Hidupkan data"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Tiada smbg Internet"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi disambungkan"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Mencari GPS"</string>
@@ -220,7 +215,7 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Dimatikan"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Tiada rangkaian disimpan tersedia"</string>
<string name="quick_settings_cast_title" msgid="1893629685050355115">"Skrin Cast"</string>
- <string name="quick_settings_casting" msgid="6601710681033353316">"Barisan Pelakon"</string>
+ <string name="quick_settings_casting" msgid="6601710681033353316">"Menghantar"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Peranti tidak bernama"</string>
<string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Bersedia untuk menghantar"</string>
<string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Tiada peranti tersedia"</string>
@@ -242,7 +237,7 @@
<string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"Melebihi had"</string>
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> digunakan"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> had"</string>
- <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> amaran"</string>
+ <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Amaran <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="recents_empty_message" msgid="7883614615463619450">"Tiada apl terbaharu"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Maklumat Aplikasi"</string>
<string name="recents_lock_to_app_button_label" msgid="4793991421811647489">"kunci ke apl"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Carian"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Luncurkan ke atas untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Luncurkan ke kiri untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Tiada gangguan, termasuk penggera"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Tiada gangguan"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Gangguan keutamaan sahaja"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Penggera anda yang seterusnya pada <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Penggera anda yang seterusnya pada <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Anda tdk akan mdgr penggera anda pd <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Pemberitahuan kurang penting di bawah"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Ketik lagi untuk membuka"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Mengecas (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> sehingga penuh)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Tetamu"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Tetamu"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Tetamu keluar"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Selama satu minit"</item>
<item quantity="other" msgid="6924190729213550991">"Selama %d minit"</item>
@@ -290,14 +294,8 @@
<string name="battery_saver_notification_text" msgid="7796554871101546872">"Prestasi peranti dikurangkan."</string>
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Buka tetapan penjimat bateri"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
- <!-- no translation found for notification_hidden_text (1135169301897151909) -->
- <skip />
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="notification_hidden_text" msgid="1135169301897151909">"Kandungan tersembunyi"</string>
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> akan mula mengabadikan semua yang dipaparkan pada skrin anda.."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Jangan tunjukkan lagi"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Mulakan sekarang"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 9f06442..7176c34 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -71,7 +71,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား ဖမ်းယူပြီး"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"သင့်ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား ကြည့်ရှုရန် ထိပါ"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား မဖမ်းစီးနိုင်ပါ"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား မသိမ်းဆည်းနိုင်ပါ သိမ်းဆည်းမှုအား အသုံးပြုနေပါလိမ့်မည်"</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB ဖိုင်ပြောင်း ရွေးမှုများ"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"မီဒီယာပလေရာအနေဖြင့် တပ်ဆင်ရန် (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"ကင်မရာအနေဖြင့် တပ်ဆင်ရန် (PTP)"</string>
@@ -266,6 +267,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> အပြည့် အထိ) အားသွင်းနေ"</string>
<string name="guest_nickname" msgid="8059989128963789678">"ဧည့်သည်"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ ဧည့်သည်"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"ဧည့်သည့် ထွက်ရန်"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"တစ်မိနစ် အတွင်း"</item>
<item quantity="other" msgid="6924190729213550991">"%d မိနစ် အတွင်း"</item>
@@ -277,7 +291,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"ဘက်ထရီ ချွေတာသူ ဆက်တင်များကို ဖွင့်ရန်"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"အကြောင်းအရာများ ဝှက်ထား"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"ဧည့်သည့် ထွက်ရန်"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> က သင်၏ မျက်နှာပြင် ပေါ်မှာ ပြသထားသည့် အရာတိုင်းကို စတင် ဖမ်းယူမည်။"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"နောက်ထပ် မပြပါနှင့်"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"ယခု စတင်ပါ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 587e119..f3d78bc 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Skjermdumpen er lagret."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Trykk for å se skjermdumpen."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Kan ikke lagre skjermdumpen."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Kan ikke ikke lagre skjermdumpen. Det er mulig ekstern lagring er i bruk."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Altern. for USB-filoverføring"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Sett inn som mediespiller (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Sett inn som kamera (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Lader (fulladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Gjest"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Gjest"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Avslutt gjesteøkten"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"I ett minutt"</item>
<item quantity="other" msgid="6924190729213550991">"I %d minutter"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Åpen innstilling for batterisparing"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g> %%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Innholdet er skjult"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Avslutt gjesteøkten"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tar opp alt som vies på skjermen din."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ikke vis igjen"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Start nå"</string>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 8d1347d..d1631ff 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"स्क्रिनसट क्याप्चर गरियो।"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"तपाईँको स्क्रिनसट हेर्न छुनुहोस्।"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"स्क्रिनसट क्याप्चर गर्न सकिएन।"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"स्क्रिनसटलाई बचत गर्न सकेन। भण्डारण उपयोगमा हुन सक्छ।"</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB फाइल सार्ने विकल्पहरू"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"मिडिया प्लेयर(MTP)को रूपमा माउन्ट गर्नुहोस्"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"क्यामेराको रूपमा माउन्ट गर्नुहोस् (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"प्यानल बन्द गर्नुहोस्"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"थप समय"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"कम समय"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G-3G डेटा बन्द छ"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G डेटा बन्द छ"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"सेलुलर डेटा बन्द छ"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"डेटा बन्द छ"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"तपाईंले सेट गर्नु भएको सीमा पुगेको हुनाले तपाईंको उपकरणले डेटा बंद गर्यो।\n\n यसलाई फिर्ता गर्दा आफ्नो वाहक बाट शुल्क लिन सक्छ।"</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"डेटा खोल्नुहोस्"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"इन्टरनेट जडान छैन"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"वाइफाइ जडित"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"GPSको लागि खोजी गर्दै"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"खोज्नुहोस्"</string>
<string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>को लागि माथि धिसार्नुहोस्"</string>
<string name="description_direction_left" msgid="7207478719805562165">"स्लाइड <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>को लागि बायाँ।"</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"बिना रोकटोक, सचेतक सहित"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"कुनै रुकावटहरू छैन"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"प्राथमिकता रुकावटहरूमा मात्र"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"तपाईंको अर्को सचेतक <xliff:g id="ALARM_TIME">%s</xliff:g> मा छ"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"तपाईंको अर्को सचेतक <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g> हो"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"<xliff:g id="ALARM_TIME">%s</xliff:g> मा तपाईंले आफ्नो सचेतक सुन्नुहुने छैन"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"तल कम जरुरी सूचनाहरू"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"खोल्न पुनः ट्याप गर्नुहोस्"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"चार्ज हुँदै (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण भएसम्म)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"अतिथि"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+अतिथि"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"अतिथि बन्द"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"एक मिनेटको लागि"</item>
<item quantity="other" msgid="6924190729213550991">"%d मिनेटको लागि"</item>
@@ -290,14 +294,8 @@
<string name="battery_saver_notification_text" msgid="7796554871101546872">"उपकरण कार्य क्षमता कम छ।"</string>
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"ब्याट्री सेभर सेटिङ्हरू खुला गर्नुहोस्"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
- <!-- no translation found for notification_hidden_text (1135169301897151909) -->
- <skip />
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="notification_hidden_text" msgid="1135169301897151909">"लुकेका सामाग्रीहरू"</string>
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले आफ्नो स्क्रीनमा प्रदर्शित हुने सबै खिच्न शुरू गर्नेछ।"</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"फेरि नदेखाउनुहोस्"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"अहिले सुरु गर्नुहोस्"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 1122224..54e590f 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot gemaakt."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Raak aan om uw screenshot te bekijken."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Screenshot is niet gemaakt."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Kan screenshot niet opslaan. Mogelijk is de opslag in gebruik."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Opties voor USB-bestandsoverdracht"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Koppelen als mediaspeler (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Koppelen als camera (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Opladen (vol over <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Gast"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Gast"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Gastmodus verlaten"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Eén minuut"</item>
<item quantity="other" msgid="6924190729213550991">"%d minuten"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Instellingen voor Accubesparing openen"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Inhoud verborgen"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Gastmodus verlaten"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> gaat alles vastleggen dat wordt weergegeven op uw scherm."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Niet opnieuw weergeven"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Nu starten"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index bfa9b12..907f861 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Wykonano zrzut ekranu."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Dotknij, aby wyświetlić zrzut ekranu."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Nie udało się wykonać zrzutu ekranu."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Nie udało się zapisać zrzutu ekranu. Pamięć może być w użyciu."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB – opcje przesyłania plików"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Podłącz jako odtwarzacz multimedialny (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Podłącz jako aparat (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Ładuje się (pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Gość"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"Dodaj gościa"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Zakończ tryb gościa"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Przez minutę"</item>
<item quantity="other" msgid="6924190729213550991">"Przez %d min"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Otwórz ustawienia oszczędzania baterii"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Treści ukryte"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Zakończ tryb gościa"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> będzie zapisywać wszystko, co wyświetli się na ekranie."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Nie pokazuj ponownie"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Rozpocznij teraz"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 65d737a..b25c1a3 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de ecrã efetuada"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Toque para ver a captura de ecrã"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Não foi possível obter captura de ecrã."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Não foi possível guardar a captura de ecrã. O armazenamento poderá estar a ser utilizado."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Opções de transm. de fich. USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Montar como leitor de multimédia (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Montar como câmara (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"A carregar (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até à carga máxima)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Convidado"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Convidado"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Sair de modo convidado"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Durante um minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Durante %d minutos"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Abrir as definições de poupança de bateria"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Conteúdo oculto"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Sair de modo convidado"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"O(a) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vai começar a captar tudo o que é apresentado no ecrã."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Não mostrar de novo"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Começar agora"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index a6df531..fc0d95a 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de tela obtida."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Toque para visualizar a captura de tela."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Não foi possível obter a captura de tela."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Não foi possível salvar a captura de tela. O armazenamento pode estar em uso."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Opções transf. arq. por USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Conectar como media player (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Montar como uma câmera (PTP)"</string>
@@ -168,18 +169,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Fechar painel"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mais tempo"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Menos tempo"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"Os dados 2G-3G foram desativados"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"Os dados 4G foram desativados"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Os dados da rede celular foram desativados"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Os dados foram desativados"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"O dispositivo desativou os dados porque o limite definido foi atingido.\n\nAtivá-los novamente poderá resultar em cobranças de sua operadora."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Ativar dados"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Sem conexão à Internet"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi conectado"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Buscando GPS"</string>
@@ -257,16 +252,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Pesquisar"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para cima."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para a esquerda."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Sem interrupções, incluindo alarmes"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Sem interrupções"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Apenas interrupções prioritárias"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Seu próximo alarme será às <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Seu próximo alarme será em <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Você não ouvirá o alarme às <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Notificações menos urgentes abaixo"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Toque novamente para abrir"</string>
@@ -280,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até concluir)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Convidado"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ convidado"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Remover convidado"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Por 1 minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Por %d minutos"</item>
@@ -293,12 +297,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Abrir configurações de economia de bateria"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Conteúdo oculto"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> começará a capturar tudo o que for exibido na tela."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Não mostrar novamente"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Iniciar agora"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 3a93f38..08c0691 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Captură de ecran realizată."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Atingeţi pentru a vedea captura de ecran."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Captura de ecran nu a putut fi realizată."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Captura de ecran nu a putut fi salvată. Este posibil să fie utilizată stocarea."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Opţiuni pentru transferul de fişiere prin USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Montaţi ca player media (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Montaţi drept cameră foto (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Închideți panoul"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mai mult timp"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mai puțin timp"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"Datele 2G-3G sunt dezactivate"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"Datele 4G sunt dezactivate"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Datele mobile sunt dezactivate"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Datele sunt dezactivate"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Dispozitivul dvs. a dezactivat conexiunea de date deoarece s-a atins limita pe care ați setat-o.\n\nReactivarea conexiunii poate genera aplicarea de taxe de către operator."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Activați datele"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Fără conex. internet"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi conectat"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Se caută GPS"</string>
@@ -219,10 +214,10 @@
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nicio reţea"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi deconectat"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Nicio rețea salvată disponibilă"</string>
- <string name="quick_settings_cast_title" msgid="1893629685050355115">"Trimiteți ecranul"</string>
- <string name="quick_settings_casting" msgid="6601710681033353316">"Se trimite"</string>
+ <string name="quick_settings_cast_title" msgid="1893629685050355115">"Proiectați ecranul"</string>
+ <string name="quick_settings_casting" msgid="6601710681033353316">"Se proiectează"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispozitiv nedenumit"</string>
- <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pregătit pentru a trimite"</string>
+ <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pregătit pentru proiecție"</string>
<string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Niciun dispozitiv disponibil"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Luminozitate"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMAT"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Căutaţi"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Glisaţi în sus pentru <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Glisaţi spre stânga pentru <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Fără întreruperi, inclusiv alarme"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Fără întreruperi"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Numai întreruperi cu prioritate"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Următoarea alarmă este setată la <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Următoarea alarmă este la <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Nu veți auzi alarma la <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Notificările mai puțin urgente mai jos"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Atingeți din nou pentru a deschide"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Se încarcă (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> până la finalizare)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Invitat"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Invitat"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Închideți invitatul"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Timp de un minut"</item>
<item quantity="other" msgid="6924190729213550991">"Timp de %d (de) minute"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Deschideți setările pentru economisirea bateriei"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Conținutul este ascuns"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> va începe să captureze tot ceea ce se afișează pe ecran."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Nu se mai afișează"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Începeți acum"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index a04e6aa..924c63e 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Скриншот сохранен"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Нажмите, чтобы просмотреть"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Не удалось сохранить скриншот."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Не удалось сохранить скриншот. Возможно, накопители заняты."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Параметры передачи через USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Подключить как мультимедийный проигрыватель (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Установить как камеру (PTP)"</string>
@@ -168,18 +169,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Закрыть панель."</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Увеличить продолжительность."</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Уменьшить продолжительность."</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"Передача данных 2G/3G отключена"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"Передача данных 4G отключена"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Передача мобильных данных отключена"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Передача данных отключена"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Передача данных отключена, поскольку достигнут установленный вами лимит.\n\nВы можете снова включить ее, однако за переданные данные оператор может взимать дополнительную плату."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Включить передачу данных"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Нет интернет-подключения"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi подключено"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Поиск GPS"</string>
@@ -257,16 +252,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Поиск"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Проведите вверх, чтобы <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Проведите влево, чтобы <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Режим \"Не беспокоить\" (звук будильника отключен)"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Не беспокоить"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Только приоритетные оповещения"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Следующий будильник: <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Следующий будильник: <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Следующий будильник: <xliff:g id="ALARM_TIME">%s</xliff:g>. Звук отключен."</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Показать менее важные оповещения"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Нажмите ещё раз, чтобы открыть"</string>
@@ -280,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Зарядка батареи (осталось <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Гость"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"Добавить гостя"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Выйти из гостевого режима"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1 мин."</item>
<item quantity="other" msgid="6924190729213550991">"%d мин."</item>
@@ -293,12 +297,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Открыть настройки режима энергосбережения"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Содержимое скрыто"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"Приложение <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> получит доступ к изображению на экране устройства."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Больше не показывать"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Начать"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 905e8ce..929ab5d 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"තිර රුව ග්රහණය කරන ලදි."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"ඔබගේ තිර රුව බැලීමට ස්පර්ශ කරන්න."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"තිර රුව ග්රහණය කිරීමට නොහැකි විය."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"තිර රුව සුරැකීමට නොහැකි විය. ආචයනය භාවිතාවේ තිබෙනවා විය හැක."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB ගොනු හුවමාරු විකල්ප"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"මධ්ය ධාවකයක් (MTP) ලෙස සවි කරන්න"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"කැමරාවක් (PTP) ලෙස සවි කරන්න"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"පැනලය වහන්න"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"වේලාව වැඩියෙන්"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"වේලාව අඩුවෙන්"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G-3G දත්ත නැත"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G දත්ත නැත"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"සෙලියුලර් දත්ත අක්රියයි"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"දත්ත අක්රියයි"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"ඔබ සකසන ලද දත්ත සීමාවට එය ළඟාවී ඇති නිසා ඔබගේ උපාංගයේ දත්ත අක්රිය කර ඇත.\n\nඑය නැවත සක්රිය කිරීමෙන් ඔබගේ වාහකය ඇතැම් විට අය කර ගැනීමට හේතු වේ."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"දත්ත සක්රිය කරන්න"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"අන්තර්ජාල සම්බන්ධතාවයක් නැත"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi සම්බන්ධිතයි"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"GPS සඳහා සොයමින්"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"සෙවීම"</string>
<string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> සඳහා උඩට සර්පණය කරන්න."</string>
<string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> සඳහා වමට සර්පණය කරන්න."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"සීනු ඇතුළුව, අතුරු බිඳීම් නැත"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"අතුරු බිදුම් නැත"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"ප්රමුඛ අතුරු බිඳීම් පමණයි"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"ඔබගේ ඊළඟ සීනුව <xliff:g id="ALARM_TIME">%s</xliff:g> තිබේ"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"ඔබගේ ඊළඟ සීනුව <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g> වේ"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"<xliff:g id="ALARM_TIME">%s</xliff:g> හි තිබෙන ඔබගේ සීනුව ඔබට ඇසෙන්නේ නැත"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"හදිසිය අඩු දැනුම් දීම් පහත"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"විවෘත කිරීමට නැවත තට්ටු කරන්න"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ආරෝපණය වෙමින් (සම්පුර්ණ වන තෙක් <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"අමුත්තා"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ අමුත්තා"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"අමුත්තා පිටවීම"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"විනාඩි එකක් සඳහා"</item>
<item quantity="other" msgid="6924190729213550991">"විනාඩි %d සඳහා"</item>
@@ -290,14 +294,8 @@
<string name="battery_saver_notification_text" msgid="7796554871101546872">"උපාංගය ක්රියාකාරිත්වය අඩු කරන ලදී."</string>
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"බැටරි ඉතිරි කරන්නා සැකසීම් විවෘත කරන්න"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
- <!-- no translation found for notification_hidden_text (1135169301897151909) -->
- <skip />
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="notification_hidden_text" msgid="1135169301897151909">"සැඟවුණු සම්බන්ධතා"</string>
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"ඔබගේ තීරයේ දර්ශනය වන සෑම දෙයම <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ලබාගැනීම ආරම්භ කරන ලදි."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"නැවත නොපෙන්වන්න"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"දැන් අරඹන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 3764406..53f2564 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Snímka obrazovky bola zaznamenaná."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Snímku obrazovky zobrazíte dotykom."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Snímku obrazovky sa nepodarilo zachytiť."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Snímku obrazovky sa nepodarilo uložiť. Ukladací priestor sa možno práve používa."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Možnosti prenosu súborov USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Pripojiť ako prehrávač médií (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Pripojiť ako fotoaparát (PTP)"</string>
@@ -168,18 +169,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Zavrieť panel"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Dlhší čas"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Kratší čas"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"dáta 2G–3G sú vypnuté"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"dáta 4G sú vypnuté"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Mobilné dáta sú vypnuté"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Dáta sú vypnuté"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Zariadenie vyplo dáta, pretože dosiahlo limit, ktorý ste nastavili.\n\nAk ich znova zapnete, môže to viesť k účtovaniu poplatkov operátora."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Zapnúť dáta"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Bez prip. na Internet"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi: pripojené"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Vyhľadávanie satelitov GPS"</string>
@@ -257,16 +252,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Vyhľadávanie"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Prejdite prstom nahor: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Prejdite prstom doľava: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Žiadne prerušenia vrátane budíkov"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Žiadne prerušenia"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Iba prioritné prerušenia"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Ďalší budík: <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Ďalší budík: <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Váš budík o <xliff:g id="ALARM_TIME">%s</xliff:g> sa nespustí"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Menej naliehavé upozornenia sa nachádzajú nižšie"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Upozornenie otvoríte opätovným klepnutím"</string>
@@ -280,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nabíja sa (úplné nabitie o <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Hosť"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"Pridať hosťa"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Ukončiť režim hosťa"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Na jednu minútu"</item>
<item quantity="other" msgid="6924190729213550991">"Na %d min"</item>
@@ -293,12 +297,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Otvorte nastavenia šetriča batérie"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g> %%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Skrytý obsah"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"Aplikácia <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> začne zaznamenávať všetok obsah zobrazený na vašej obrazovke."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Nabudúce nezobrazovať"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Spustiť"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index d73fff8..22914b0 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Posnetek zaslona je shranjen."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Dotaknite se, če si želite ogledati posnetek zaslona."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Posnetka zaslona ni bilo mogoče shraniti."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Posnetka zaslona ni bilo mogoče shraniti. Shramba je morda v uporabi."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Možnosti prenosa datotek prek USB-ja"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Vpni kot predvajalnik (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Vpni kot fotoaparat (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Polnjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napolnjenosti)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Gost"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"Dodajanje gosta"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Izhod iz načina za goste"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Za eno minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Za %d min"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Odpri nastavitve varčevanja z energijo akumulatorja"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Vsebina je skrita"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Izhod iz načina za goste"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"Aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bo začela zajemati vse, kar je prikazano na zaslonu."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Tega ne prikaži več"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Začni zdaj"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 3b2f1b9..fd66ff3 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Снимак екрана је направљен."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Додирните да бисте видели снимак екрана."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Није могуће направити снимак екрана."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Није могуће сачувати снимак екрана. Могуће је да је меморија у употреби."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Опције USB преноса датотека"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Прикључи као медија плејер (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Прикључи као камеру (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Затворите таблу"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Више времена"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Мање времена"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G–3G подаци су искључени"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G подаци су искључени"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Мобилни пренос података је искључен"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Пренос података је искључен"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Уређај је искључио пренос података јер је достигао ограничење које сте поставили.\n\nАко га поново укључите, можда ће вам мобилни оператер наплатити трошкове."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Укључи пренос података"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Нема интернет везе"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi је повезан"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Тражи се GPS"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Претрага"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Превуците нагоре за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Превуците улево за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Без прекида, укључујући аларме"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Без прекида"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Само приоритетни прекиди"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Следећи аларм је у <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Следећи аларм: <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Нећете чути аларм у <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Мање хитна обавештења су у наставку"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Додирните поново да бисте отворили"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Пуњење (пун је за <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Гост"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Гост"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Изађи из режима госта"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Један минут"</item>
<item quantity="other" msgid="6924190729213550991">"%d мин"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Отворите подешавања Штедње батерије"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Садржај је сакривен"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ће почети да снима све што се приказује на екрану."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Не приказуј поново"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Започни одмах"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 4e3caa1..69c47e4 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Skärmdumpen har tagits."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Tryck här om du vill visa skärmdumpen."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Det gick inte att ta någon skärmdump."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Det gick inte att spara skärmdumpen. Extern lagring kanske används."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Överföringsalternativ"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Montera som mediaspelare (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Montera som kamera (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Laddar (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> tills batteriet är fulladdat)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Gäst"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"Lägg till gäst"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Avsluta gäst"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"I en minut"</item>
<item quantity="other" msgid="6924190729213550991">"I %d minuter"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Öppna inställningarna för batterisparläget"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Innehåll har dolts"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Avsluta gäst"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tar en bild av allt som visas på skärmen."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Visa inte igen"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Starta nu"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 9a221ea..9aa90496 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -71,7 +71,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Picha ya skrini imenaswa."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Gusa ili kuona picha yako ya skrini."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Haikuweza kunasa picha ya skrini"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Haikuweza kuhifadhi picha ya skrini. Huenda hifadhi inatumika."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Machaguo ya uhamisho wa faili la USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Angika kama kichezeshi cha midia (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Angika kama kamera (PTP)"</string>
@@ -168,7 +169,7 @@
<string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"Data ya 4G imezimwa"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Data ya simu ya mkononi imezimwa"</string>
<string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Data imezimwa"</string>
- <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Kifaa chako kilizima data kwa sababu kilifikia kikomo ulichoweka.\n\nKukiwasha tena kunaweza kupelekea utozwe na mtoa huduma wako."</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Kifaa chako kilizima data kwa sababu kilifikia kikomo ulichoweka.\n\nKuwasha data tena kunaweza kusababisha matozo kutoka kwa mtoa huduma wako."</string>
<string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Washa matumizi ya data"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Hakuna muunganisho wa mtandao"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Mtandao-hewa umeunganishwa"</string>
@@ -214,7 +215,7 @@
<string name="quick_settings_cast_title" msgid="1893629685050355115">"Tuma skrini"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Inatuma"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Kifaa kisichokuwa na jina"</string>
- <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Tayari kwa kutuma"</string>
+ <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Tayari kutuma"</string>
<string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Hakuna vifaa vilivyopatikana"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ung\'avu"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"KIOTOMATIKI"</string>
@@ -247,7 +248,7 @@
<string name="description_target_search" msgid="3091587249776033139">"Tafuta"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Sogeza juu kwa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Sogeza kushoto kwa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
- <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Hakuna katizo, ikiwa ni pamoja na kengele"</string>
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Hakuna kukatizwa, hata kutoka kwenye kengele"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Hakuna katizo"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Katizo za kipaumbele pekee"</string>
<string name="zen_alarm_information_time" msgid="5235772206174372272">"Kengele yako inayofuata itakuwa saa <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
@@ -266,6 +267,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Inachaji ( <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hadi ijae)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Aliyealikwa"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Aliyealikwa"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Ondoa aliyealikwa"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Kwa dakika moja"</item>
<item quantity="other" msgid="6924190729213550991">"Kwa dakika %d"</item>
@@ -279,7 +293,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Fungua mipangilio ya hali inayookoa betri"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Maudhui yamefichwa"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Ondoa aliyealikwa"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> itaanza kupiga picha kila kitu kinachoonyeshwa kwenye skrini yako."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Usionyeshe tena"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Anza sasa"</string>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index ab76f71..e9b0394 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"ஸ்கிரீன் ஷாட் எடுக்கப்பட்டது."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"உங்கள் ஸ்க்ரீன் ஷாட்டைப் பார்க்க தொடவும்."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ஸ்க்ரீன் ஷாட்டை எடுக்க முடியவில்லை."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"ஸ்கீர்ன் ஷாட்டைச் சேமிக்க முடியவில்லை. சேமிப்பிடம் பயன்பாட்டில் இருக்கலாம்."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB கோப்பு இடமாற்ற விருப்பங்கள்"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"(MTP) மீடியா பிளேயராக ஏற்று"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"(PTP) கேமராவாக ஏற்று"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"சார்ஜாகிறது (முழு சார்ஜிற்கு <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ஆகும்)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"அழைக்கப்பட்டவர்"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ அழைக்கப்பட்டவர்"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"விருந்தினரிலிருந்து வெளியேறு"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"ஒரு நிமிடம்"</item>
<item quantity="other" msgid="6924190729213550991">"%d நிமிடங்கள்"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"பேட்டரி சேமிப்பான் அமைப்புகளைத் திற"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"மறைந்துள்ள உள்ளடக்கம்"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"விருந்தினரிலிருந்து வெளியேறு"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"திரையில் காட்டப்படும் அனைத்தையும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> படமெடுக்கும்."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"மீண்டும் காட்டாதே"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"இப்போது தொடங்கு"</string>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index cbab00f..687a110 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"స్క్రీన్షాట్ క్యాప్చర్ చేయబడింది."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"మీ స్క్రీన్షాట్ను వీక్షించడానికి తాకండి."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"స్క్రీన్షాట్ను క్యాప్చర్ చేయడం సాధ్యపడలేదు."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"స్క్రీన్షాట్ను సేవ్ చేయడం సాధ్యపడలేదు. నిల్వ ఉపయోగంలో ఉండవచ్చు."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB ఫైల్ బదిలీ ఎంపికలు"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"మీడియా ప్లేయర్గా (MTP) మౌంట్ చేయి"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"కెమెరాగా (PTP) మౌంట్ చేయి"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ఛార్జ్ అవుతోంది (పూర్తిగా నిండటానికి <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"అతిథి"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ అతిథి"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"అతిథి మోడ్ నుండి నిష్క్రమించండి"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"ఒక నిమిషానికి"</item>
<item quantity="other" msgid="6924190729213550991">"%d నిమిషాలకి"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"బ్యాటరీ సేవర్ సెట్టింగ్లను తెరువు"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"కంటెంట్లు దాచబడ్డాయి"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"అతిథి మోడ్ నుండి నిష్క్రమించండి"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> మీ స్క్రీన్పై కనిపించే ప్రతిదాన్ని క్యాప్చర్ చేయడం ప్రారంభిస్తుంది."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"మళ్లీ చూపవద్దు"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"ఇప్పుడే ప్రారంభించు"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 7ebfda6..4f2cb6f 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"จับภาพหน้าจอแล้ว"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"แตะเพื่อดูภาพหน้าจอของคุณ"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ไม่สามารถจับภาพหน้าจอ"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"ไม่สามารถบันทึกภาพหน้าจอ ที่จัดเก็บข้อมูลอาจมีการใช้งานอยู่"</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"ตัวเลือกการถ่ายโอนไฟล์ USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"ต่อเชื่อมเป็นโปรแกรมเล่นสื่อ (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"ต่อเชื่อมเป็นกล้องถ่ายรูป (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"ปิดแผงควบคุม"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"เวลามากขึ้น"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"เวลาน้อยลง"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"ข้อมูล 2G-3G ปิดอยู่"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"ข้อมูล 4G ปิดอยู่"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"ข้อมูลมือถือปิดอยู่"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"ข้อมูลปิดอยู่"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"อุปกรณ์ของคุณปิดการใช้ข้อมูลเนื่องจากถึงขีดจำกัดที่คุณตั้งค่าไว้\n\nผู้ให้บริการอาจเรียกเก็บเงินหากเปิดใช้อีกครั้ง"</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"เปิดใช้ข้อมูล"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"ไม่มีอินเทอร์เน็ต"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"เชื่อมต่อ WiFi แล้ว"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"กำลังค้นหา GPS"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"ค้นหา"</string>
<string name="description_direction_up" msgid="7169032478259485180">"เลื่อนขึ้นเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
<string name="description_direction_left" msgid="7207478719805562165">"เลื่อนไปทางซ้ายเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"ไม่มีการรบกวนรวมถึงเสียงปลุก"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"ไม่มีการรบกวน"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"เฉพาะเรื่องสำคัญเท่านั้น"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"การปลุกครั้งถัดไปของคุณคือเวลา <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"การปลุกครั้งถัดไปของคุณคือ <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"คุณจะไม่ได้ยินเสียงปลุกในเวลา <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"การแจ้งเตือนที่เร่งด่วนน้อยด้านล่าง"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"แตะอีกครั้งเพื่อเปิด"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"กำลังชาร์จ (อีก <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> เต็ม)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"ผู้เข้าร่วม"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ ผู้เข้าร่วม"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"ออกจากโหมดผู้เยี่ยมชม"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1 นาที"</item>
<item quantity="other" msgid="6924190729213550991">"%d นาที"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"เปิดการตั้งค่าโหมดประหยัดแบตเตอรี่"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"เนื้อหาที่ซ่อน"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะเริ่มจับภาพทุกอย่างที่แสดงบนหน้าจอ"</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"ไม่ต้องแสดงข้อความนี้อีก"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"เริ่มเลย"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 0e3e7dd..4a6007c 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Nakuha ang screenshot."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Pindutin upang tingnan ang iyong screenshot."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Hindi makuha ang screenshot."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Hindi ma-save ang screenshot. Maaaring ginagamit ang storage."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Opsyon paglipat ng USB file"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"I-mount bilang isang media player (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"I-mount bilang camera (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Isara ang panel"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Higit pang oras"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mas kaunting oras"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"Naka-off ang 2G-3G data"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"Naka-off ang 4G data"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Naka-off ang cellular data"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Naka-off ang data"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Ini-off ng device mo ang data dahil naabot na nito ang limitasyong iyong itinakda.\n\nAng muling pag-o-on dito ay maaaring humantong sa mga singiling magmumula sa iyong carrier."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"I-on ang data"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Walang koneksyon sa Internet"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"nakakonekta ang Wi-Fi"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Naghahanap ng GPS"</string>
@@ -197,8 +192,7 @@
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> (na) Device)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Naka-off ang Bluetooth"</string>
- <!-- no translation found for quick_settings_bluetooth_detail_empty_text (4910015762433302860) -->
- <skip />
+ <string name="quick_settings_bluetooth_detail_empty_text" msgid="4910015762433302860">"Walang available na mga magkapares na device"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brightness"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Awtomatikong i-rotate"</string>
<string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Naka-lock ang pag-ikot"</string>
@@ -213,26 +207,18 @@
<string name="quick_settings_settings_label" msgid="5326556592578065401">"Mga Setting"</string>
<string name="quick_settings_time_label" msgid="4635969182239736408">"Oras"</string>
<string name="quick_settings_user_label" msgid="5238995632130897840">"Ako"</string>
- <!-- no translation found for quick_settings_user_title (4467690427642392403) -->
- <skip />
- <!-- no translation found for quick_settings_user_new_user (9030521362023479778) -->
- <skip />
+ <string name="quick_settings_user_title" msgid="4467690427642392403">"User"</string>
+ <string name="quick_settings_user_new_user" msgid="9030521362023479778">"Bagong user"</string>
<string name="quick_settings_wifi_label" msgid="9135344704899546041">"Wi-Fi"</string>
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Hindi Nakakonekta"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Walang Network"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Naka-off ang Wi-Fi"</string>
- <!-- no translation found for quick_settings_wifi_detail_empty_text (2831702993995222755) -->
- <skip />
- <!-- no translation found for quick_settings_cast_title (1893629685050355115) -->
- <skip />
- <!-- no translation found for quick_settings_casting (6601710681033353316) -->
- <skip />
- <!-- no translation found for quick_settings_cast_device_default_name (5367253104742382945) -->
- <skip />
- <!-- no translation found for quick_settings_cast_device_default_description (2484573682378634413) -->
- <skip />
- <!-- no translation found for quick_settings_cast_detail_empty_text (311785821261640623) -->
- <skip />
+ <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Walang available na naka-save na mga network"</string>
+ <string name="quick_settings_cast_title" msgid="1893629685050355115">"I-cast ang screen"</string>
+ <string name="quick_settings_casting" msgid="6601710681033353316">"Nagka-cast"</string>
+ <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Walang pangalang device"</string>
+ <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Handang mag-cast"</string>
+ <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Walang available na mga device"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"I-invert ang mga kulay"</string>
@@ -240,8 +226,7 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"Marami pang setting"</string>
<string name="quick_settings_done" msgid="3402999958839153376">"Tapos na"</string>
<string name="quick_settings_connected" msgid="1722253542984847487">"Nakakonekta"</string>
- <!-- no translation found for quick_settings_connecting (47623027419264404) -->
- <skip />
+ <string name="quick_settings_connecting" msgid="47623027419264404">"Kumokonekta..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Nagte-tether"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Mga Notification"</string>
@@ -252,8 +237,7 @@
<string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"Lumampas sa limitasyon"</string>
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> ang nagamit"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ang limitasyon"</string>
- <!-- no translation found for quick_settings_cellular_detail_data_warning (2440098045692399009) -->
- <skip />
+ <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Babala sa <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="recents_empty_message" msgid="7883614615463619450">"Walang kamakailang mga app"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Impormasyon ng Application"</string>
<string name="recents_lock_to_app_button_label" msgid="4793991421811647489">"lock to app"</string>
@@ -266,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Maghanap"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Mag-slide pataas para sa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Mag-slide pakaliwa para sa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Walang mga pagkaantala, kabilang na ang mga alarma"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Walang mga paggambala"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Mga may priyoridad na paggambala lang"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Ang susunod mong alarma ay sa <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Ang susunod mong alarma ay <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Hindi mo maririnig ang alarma mo ng <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Nasa ibaba ang mga notification na hindi masyadong mahalaga"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"I-tap ulit upang buksan"</string>
@@ -289,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nagtsa-charge (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hanggang mapuno)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Bisita"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Bisita"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Lumabas bilang guest"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Sa loob ng isang minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Sa loob ng %d (na) minuto"</item>
@@ -302,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Buksan ang mga setting ng tagatipid ng baterya"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Nakatago ang mga content"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"Sisimulan ng i-capture ng <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ang lahat ng ipinapakita sa iyong screen."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Huwag ipakitang muli"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Magsimula ngayon"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 7a785d6..7938f3d 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Ekran görüntüsü alındı."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Ekran görüntünüzü izlemek için dokunun."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Ekran görüntüsü alınamadı."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Ekran görüntüsü kaydedilemedi. Depolama birimi kullanımda olabilir."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB dosya aktarım seçenekleri"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Medya oynatıcı olarak ekle (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Kamera olarak ekle (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Paneli kapatın"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Daha uzun süre"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Daha kısa süre"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G-3G veri kullanımı kapalı"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G veri kullanımı kapalı"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Hücresel veri kapalı"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Veri kullanımı kapalı"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Ayarladığınız sınıra erişildiğinden cihazınız veri kullanımını kapattı.\n\nVeri kullanımını tekrar açmanız operatörünüzün ek ödemeler almasına neden olabilir."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Veri kullanımını aç"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"İnternet bağlantısı yok"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Kablosuz bağlandı"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"GPS aranıyor"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Ara"</string>
<string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> için yukarı kaydırın."</string>
<string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> için sola kaydırın."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Alarmlar dahil hiç kesinti yok"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Kesinti yok"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Sadece öncelikli kesintiler"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Bir sonraki alarmın saati: <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Bir sonraki alarmınız: <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"<xliff:g id="ALARM_TIME">%s</xliff:g> olarak ayarlanan alarmı duymayacaksınız"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Daha az acil bildirimler aşağıdadır"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Açmak için tekrar hafifçe vurun"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Şarj oluyor (tamamen dolmasına <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kaldı)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Misafir"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Misafir"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Misafir oturumundan çık"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Bir dakika süreyle"</item>
<item quantity="other" msgid="6924190729213550991">"%d dakika süreyle"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Pil tasarrufu ayarlarını aç"</string>
<string name="battery_level_template" msgid="1609636980292580020">"%%<xliff:g id="LEVEL">%d</xliff:g>"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"İçerik gizlendi"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, ekranınızda görüntülenen her şeyi kaydetmeye başlayacak."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Bir daha gösterme"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Şimdi başla"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 9e453b0..c2259f8 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Знімок екрана зроблено."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Торкніться, щоб переглянути знімок екрана."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Не вдалося зробити знімок екрана."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Не вдалося зберегти знімок екрана. Можливо, пам’ять використовується."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Парам.передав.файлів через USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Підключити як медіапрогравач (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Підключити як камеру (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Закрити панель"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Більше часу"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Менше часу"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"Дані 2G–3G вимкнено"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"Дані 4G вимкнено"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Мобільні дані вимкнено"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Дані вимкнено"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Пристрій вимкнув передавання даних, оскільки перевищено встановлений вами ліміт.\n\nЯкщо передавання даних знову ввімкнути, оператор може стягувати додаткову плату."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Увімкнути дані"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Немає з’єднання"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi під’єднано"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Виконується пошук GPS-сигналу"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Пошук"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Проведіть пальцем угору, щоб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Проведіть пальцем ліворуч, щоб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Без сповіщень, зокрема сигналів будильника"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Без сповіщень"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Лише пріоритетні сповіщення"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Наступний сигнал: <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Наступний сигнал: <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Сигнал не лунатиме о <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Менше термінових сповіщень нижче"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Торкніться знову, щоб відкрити"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Заряджання (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до повного зарядження)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Гість"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"Додати гостя"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Вийти з режиму гостя"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Протягом хвилини"</item>
<item quantity="other" msgid="6924190729213550991">"Протягом %d хв"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Відкрийте налаштування режиму заощадження заряду акумулятора"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Вміст сховано"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> отримає доступ до всіх даних, які відображаються на вашому екрані."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Більше не показувати"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Почати зараз"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index 379fb1b..e2e1704 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"اسکرین شاٹ کیپچر کیا گیا۔"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"اپنے اسکرین شاٹ دیکھنے کیلئے چھوئیں۔"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"اسکرین شاٹ کیپچر نہیں کر سکے۔"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"اسکرین شاٹ محفوظ نہیں کر سکے۔ ممکن ہے اسٹوریج کا استعمال ہو رہا ہے۔"</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB فائل منتقل کرنیکے اختیارات"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"ایک میڈیا پلیئر (MTP) کے بطور ماؤنٹ کریں"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"ایک کیمرہ (PTP) کے بطور ماؤنٹ کریں"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"پینل بند کریں"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"مزید وقت"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"کم وقت"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G-3G ڈیٹا آف ہے"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G ڈیٹا آف ہے"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"سیلولر ڈیٹا آف ہے"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"ڈیٹا آف ہے"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"آپ کے آلہ نے ڈیٹا کو آف کر دیا کیونکہ یہ آپ کی متعینہ حد کو پہنچ گیا۔\n\nاسے دوبارہ آن کرنے سے آپ کے کیریئر کی جانب سے چارجز عائد ہو سکتے ہیں۔"</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"ڈیٹا آن کریں"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"کوئی انٹرنیٹ کنکشن نہیں"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi مربوط ہے"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"GPS کی تلاش کر رہا ہے"</string>
@@ -197,7 +192,7 @@
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"بلوٹوتھ"</string>
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"بلوٹوتھ (<xliff:g id="NUMBER">%d</xliff:g> آلات)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"بلوٹوتھ آف ہے"</string>
- <string name="quick_settings_bluetooth_detail_empty_text" msgid="4910015762433302860">"کوئی جوڑ بنائے ہوئے آلات دستیاب نہیں ہیں"</string>
+ <string name="quick_settings_bluetooth_detail_empty_text" msgid="4910015762433302860">"کوئی جوڑا بنائے ہوئے آلات دستیاب نہیں ہیں"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"چمکیلا پن"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"خود کار طور پر گھمائیں"</string>
<string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"گردش مقفل ہے"</string>
@@ -242,7 +237,7 @@
<string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"حد سے زیادہ"</string>
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> استعمال کردہ"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> حد"</string>
- <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> انتباہ"</string>
+ <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> وارننگ"</string>
<string name="recents_empty_message" msgid="7883614615463619450">"کوئی حالیہ ایپس نہیں ہیں"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ایپلیکیشن کی معلومات"</string>
<string name="recents_lock_to_app_button_label" msgid="4793991421811647489">"لاک ٹو ایپ"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"تلاش کریں"</string>
<string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> کیلئے اوپر سلائیڈ کریں۔"</string>
<string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> کیلئے بائیں سلائیڈ کریں۔"</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"الارمز کے بشمول، کوئی مداخلتیں نہیں ہیں"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"کوئی مداخلتیں نہیں ہیں"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"صرف ترجیحی مداخلتیں"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"آپ کا اگلا الارم <xliff:g id="ALARM_TIME">%s</xliff:g> بجے ہے"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"آپ کا اگلا الارم <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g> ہے"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"آپ کو <xliff:g id="ALARM_TIME">%s</xliff:g> بجے اپنا الارم سنائی نہیں دیگا"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"کم اہم اطلاعات ذیل میں ہیں"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"کھولنے کیلئے دوبارہ تھپتھپائیں"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"چارج ہو رہا ہے (مکمل ہونے تک <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> باقی ہیں)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"مہمان"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ مہمان"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"مہمان وضع سے باہر نکلیں"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"ایک منٹ کیلئے"</item>
<item quantity="other" msgid="6924190729213550991">"%d منٹ کیلئے"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"بیٹری سیور کی ترتیبات کھولیں"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"مواد مخفی ہیں"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> آپ کی اسکرین پر ڈسپلے ہونے والی ہر چیز کو کیپچر کرنا شروع کر دیگی۔"</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"دوبارہ نہ دکھائیں"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"ابھی شروع کریں"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 7a551d3..cdc55e6 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Ekran surati olindi."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Ekraningiz suratini ko‘rish uchun bosing."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Ekran surati olinmadi."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Ekran surati saqlanmadi. Xotiradan foydalanilayotgan bo‘lishi mumkin."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB fayl ko‘chirish moslamalari"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Media pleyer sifatida ulash (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Kamera sifatida ulash (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Panelni yopish"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Vaqtni ko‘paytirish"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Vaqtni kamaytirish"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G/3G internet o‘chirib qo‘yildi"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G internet o‘chirib qo‘yildi"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Mobil internet o‘chirib qo‘yildi"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Internet o‘chirib qo‘yildi"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Siz o‘rnatgan chegaraga yetib kelgani tufayli qurilmangizda internet o‘chirib qo‘yildi.\n\nUni qayta yoqishingiz mumkin, biroq buning uchun aloqa operatoringiz qo‘shimcha haq olishi mumkin."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Internetni yoqish"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Internetga ulanmagan"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi ulandi"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"GPS qidirilmoqda"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Izlash"</string>
<string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun yuqoriga suring."</string>
<string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun chapga suring."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"“Bezovta qilmaslik” rejimi (uyg‘otkich ovozi o‘chirilgan)"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Tanaffuslarsiz"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Faqat ustuvor tanaffuslar"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Keyingi uyg‘otkich: <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Keyingi uyg‘otkich: <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Keyingi uyg‘otkich: <xliff:g id="ALARM_TIME">%s</xliff:g>. Ovoz eshitilmaydi."</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Kam ahamiyatli bildirishnomalarni pastda ko‘rsatish"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Ochish uchun yana bosing"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Quvvat olmoqda (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>da to‘ladi)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Mehmon"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Mehmon"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Mehmon rejimidan chiqish"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1 daqiqa"</item>
<item quantity="other" msgid="6924190729213550991">"%d daqiqa"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Quvvat tejash sozlamalarini ochish"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Kontent yashirildi"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ilovasi qurilma ekranidagi har qanday tasvirni ko‘rishni boshlaydi."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Boshqa ko‘rsatilmasin"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Boshlash"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index d413ed7..c39ec02 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Đã chụp ảnh màn hình."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Chạm để xem ảnh chụp màn hình của bạn."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Không thể chụp ảnh màn hình."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Không thể lưu ảnh chụp màn hình. Bộ lưu trữ có thể đang được sử dụng."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Tùy chọn truyền tệp USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Gắn như một trình phát đa phương tiện (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Gắn như một máy ảnh (PTP)"</string>
@@ -166,18 +167,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Đóng bảng điều khiển"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Nhiều thời gian hơn"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Ít thời gian hơn"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"Dữ liệu 2G-3G bị tắt"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"Dữ liệu 4G bị tắt"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"Dữ liệu di động bị tắt"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"Dữ liệu bị tắt"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"Thiết bị của bạn đã tắt dữ liệu do đã đạt đến giới hạn bạn đã đặt.\n\nViệc bật lại dữ liệu có thể dẫn tới các khoản phí từ nhà cung cấp dịch vụ của bạn."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"Bật dữ liệu"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Ko có k.nối Internet"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Đã kết nối Wi-Fi"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Đang tìm kiếm GPS"</string>
@@ -255,16 +250,12 @@
<string name="description_target_search" msgid="3091587249776033139">"Tìm kiếm"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Trượt lên để <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Trượt sang trái để <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"Không có gián đoạn, bao gồm báo thức"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"Không có gián đoạn nào"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"Chỉ các gián đoạn ưu tiên"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"Lần báo thức tiếp theo của bạn vào lúc <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Lần báo thức tiếp theo của bạn là <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"Bạn sẽ không nghe thấy báo thức lúc <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Thông báo ít khẩn cấp hơn bên dưới"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Nhấn lại để mở"</string>
@@ -278,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Đang sạc (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> cho đến khi đầy)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Khách"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Khách"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Thoát chế độ khách"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Trong một phút"</item>
<item quantity="other" msgid="6924190729213550991">"Trong %d phút"</item>
@@ -291,12 +295,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Mở cài đặt trình tiết kiệm pin"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Nội dung bị ẩn"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ bắt đầu chụp mọi thứ hiển thị trên màn hình."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Không hiển thị lại"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Bắt đầu ngay"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 2d88348..111e0a2 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"已抓取屏幕截图。"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"触摸可查看您的屏幕截图。"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"无法抓取屏幕截图。"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"无法保存屏幕截图。存储设备可能正在使用中。"</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB 文件传输选项"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"作为媒体播放器 (MTP) 装载"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"作为摄像头 (PTP) 装载"</string>
@@ -138,7 +139,7 @@
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"漫游中"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE"</string>
<string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"WLAN"</string>
- <string name="accessibility_no_sim" msgid="8274017118472455155">"无 SIM 卡。"</string>
+ <string name="accessibility_no_sim" msgid="8274017118472455155">"无SIM卡。"</string>
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"蓝牙网络共享。"</string>
<string name="accessibility_airplane_mode" msgid="834748999790763092">"飞行模式。"</string>
<!-- String.format failed for translation -->
@@ -168,18 +169,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"关闭面板"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"更长时间"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"更短时间"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G-3G数据网络已关闭"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G数据网络已关闭"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"移动数据网络已关闭"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"数据网络已关闭"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"由于数据流量已达到您所设置的上限,因此您的设备已关闭数据网络。\n\n如果重新开启数据网络,那么您的运营商可能会向您收取相应费用。"</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"开启数据网络"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"未连接互联网"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"WLAN 已连接"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"正在搜索 GPS"</string>
@@ -224,7 +219,7 @@
<string name="quick_settings_cast_title" msgid="1893629685050355115">"投射屏幕"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"正在投射"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"未命名设备"</string>
- <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"准备好投射"</string>
+ <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"已准备好投射"</string>
<string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"没有可用设备"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自动"</string>
@@ -239,7 +234,7 @@
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"手电筒"</string>
<string name="quick_settings_cellular_detail_title" msgid="8575062783675171695">"移动数据网络"</string>
- <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"数据流量消耗"</string>
+ <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"流量使用情况"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"剩余流量"</string>
<string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"超出上限"</string>
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"已使用<xliff:g id="DATA_USED">%s</xliff:g>"</string>
@@ -257,16 +252,12 @@
<string name="description_target_search" msgid="3091587249776033139">"搜索"</string>
<string name="description_direction_up" msgid="7169032478259485180">"向上滑动以<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
<string name="description_direction_left" msgid="7207478719805562165">"向左滑动以<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"禁止打扰(包括闹钟)"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"禁止打扰"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"仅限优先打扰内容"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"下次闹钟响铃时间:<xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"下次闹钟响铃时间:<xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"您在<xliff:g id="ALARM_TIME">%s</xliff:g>将不会听到闹钟响铃"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"不太紧急的通知会显示在下方"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"再次点按即可打开"</string>
@@ -280,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"正在充电(还需<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>才能充满)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"访客"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"添加新访客"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"退出访客模式"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1分钟"</item>
<item quantity="other" msgid="6924190729213550991">"%d分钟"</item>
@@ -292,14 +296,8 @@
<string name="battery_saver_notification_text" msgid="7796554871101546872">"设备性能已减降。"</string>
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"打开节电助手设置"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
- <!-- no translation found for notification_hidden_text (1135169301897151909) -->
- <skip />
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="notification_hidden_text" msgid="1135169301897151909">"内容已隐藏"</string>
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>将开始截取您的屏幕上显示的所有内容。"</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"不再显示"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"立即开始"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index f4c4e14..298aac1 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"已擷取螢幕畫面。"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"輕觸即可查看螢幕擷取畫面。"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"無法擷取螢幕畫面。"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"無法儲存螢幕擷取畫面,儲存裝置可能正在使用。"</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB 檔案傳輸選項"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"掛接為媒體播放器 (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"掛接為相機 (PTP)"</string>
@@ -168,18 +169,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"關閉面板"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"更多時間"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"較少時間"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G-3G 數據連線已關閉"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G 數據連線已關閉"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"流動數據連線已關閉"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"數據連線已關閉"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"數據流量已達到您所設定的上限,因此裝置關閉了數據連線。\n\n若重新開啟數據連線,流動網絡供應商可能會向您收費。"</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"開啟數據連線"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"沒有互聯網連線"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi 已連線"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"正在搜尋 GPS"</string>
@@ -257,16 +252,12 @@
<string name="description_target_search" msgid="3091587249776033139">"搜尋"</string>
<string name="description_direction_up" msgid="7169032478259485180">"向上滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
<string name="description_direction_left" msgid="7207478719805562165">"向左滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"不允許干擾 (包含鬧鐘)"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"不允許干擾"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"僅限優先干擾"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"下次鬧鐘時間:<xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"下次鬧鐘時間:<xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"您不會聽到<xliff:g id="ALARM_TIME">%s</xliff:g> 的鬧鐘"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"還有 <xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g> 則通知"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"不太緊急的通知會在下方顯示"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"再次輕按即可開啟"</string>
@@ -280,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後完成充電)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"訪客"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"新增訪客"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"結束訪客模式"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1 分鐘"</item>
<item quantity="other" msgid="6924190729213550991">"%d 分鐘"</item>
@@ -293,12 +297,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"開啟省電設定"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"內容已隱藏"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 將開始擷取您的螢幕上顯示的內容。"</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"不用再顯示"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"立即開始"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 2ffa320..d766aca 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"已拍攝螢幕擷取畫面。"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"輕觸即可查看螢幕擷取畫面。"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"無法拍攝螢幕擷取畫面。"</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"無法儲存螢幕擷取畫面,儲存空間可能正在使用中。"</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"USB 檔案傳輸選項"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"掛接為媒體播放器 (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"掛接為相機 (PTP)"</string>
@@ -168,18 +169,12 @@
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"關閉面板"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"更多時間"</string>
<string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"更少時間"</string>
- <!-- no translation found for data_usage_disabled_dialog_3g_title (2626865386971800302) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_4g_title (4629078114195977196) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_mobile_title (5793456071535876132) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_title (8723412000355709802) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog (6468718338038876604) -->
- <skip />
- <!-- no translation found for data_usage_disabled_dialog_enable (5538068036107372895) -->
- <skip />
+ <string name="data_usage_disabled_dialog_3g_title" msgid="2626865386971800302">"2G-3G 數據連線已關閉"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="4629078114195977196">"4G 數據連線已關閉"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="5793456071535876132">"行動數據連線已關閉"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="8723412000355709802">"數據連線已關閉"</string>
+ <string name="data_usage_disabled_dialog" msgid="6468718338038876604">"數據流量已達您所設定的上限,因此裝置關閉了數據連線。\n\n如果重新開啟數據連線,行動通訊業者可能會向您收費。"</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="5538068036107372895">"開啟數據連線"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"沒有網際網路連線"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi 已連線"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"正在搜尋 GPS"</string>
@@ -257,16 +252,12 @@
<string name="description_target_search" msgid="3091587249776033139">"搜尋"</string>
<string name="description_direction_up" msgid="7169032478259485180">"向上滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
<string name="description_direction_left" msgid="7207478719805562165">"向左滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <!-- no translation found for zen_no_interruptions_with_warning (2522931836819051293) -->
- <skip />
+ <string name="zen_no_interruptions_with_warning" msgid="2522931836819051293">"不允許干擾 (包含鬧鐘)"</string>
<string name="zen_no_interruptions" msgid="7970973750143632592">"不允許干擾"</string>
<string name="zen_important_interruptions" msgid="3477041776609757628">"僅限優先干擾"</string>
- <!-- no translation found for zen_alarm_information_time (5235772206174372272) -->
- <skip />
- <!-- no translation found for zen_alarm_information_day_time (8422733576255047893) -->
- <skip />
- <!-- no translation found for zen_alarm_warning (6873910860111498041) -->
- <skip />
+ <string name="zen_alarm_information_time" msgid="5235772206174372272">"下次鬧鐘時間:<xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"下次鬧鐘時間:<xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="6873910860111498041">"您不會聽到<xliff:g id="ALARM_TIME">%s</xliff:g> 的鬧鐘"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"還有 <xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g> 則通知"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"較不緊急的通知會顯示在下方"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"再次輕按即可開啟"</string>
@@ -280,6 +271,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後充飽)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"訪客"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"新增訪客"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"結束訪客模式"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1 分鐘"</item>
<item quantity="other" msgid="6924190729213550991">"%d 分鐘"</item>
@@ -293,12 +297,7 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"開啟節約耗電量設定"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"內容已隱藏"</string>
- <!-- no translation found for guest_exit_guest (1619100760451149682) -->
- <skip />
- <!-- no translation found for media_projection_dialog_text (3071431025448218928) -->
- <skip />
- <!-- no translation found for media_projection_remember_text (3103510882172746752) -->
- <skip />
- <!-- no translation found for media_projection_action_text (8470872969457985954) -->
- <skip />
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 將開始擷取您的螢幕上顯示的內容。"</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"不要再顯示"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"立即開始"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 38e4d8f..7e49f6e 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -73,7 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Umfanekiso weskrini uqoshiwe"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Thinta ukubona imifanekiso yakho yeskrini"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Yehlulekile ukulondoloza umfanekiso weskrini."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Ayikwazanga ukulondoloza isithombe-skrini. Isitoreji sangaphandle kungenzeka kuyasetshenziswa."</string>
+ <!-- no translation found for screenshot_failed_text (1260203058661337274) -->
+ <skip />
<string name="usb_preference_title" msgid="6551050377388882787">"Okukhethwa kokudluliswa kwefayela ye-USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Lengisa njengesidlali semediya (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Lengisa ikhamera (PTP)"</string>
@@ -268,6 +269,19 @@
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Iyashaja (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ize igcwale)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Isihambeli"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ Isihambeli"</string>
+ <string name="guest_exit_guest" msgid="1619100760451149682">"Phuma kusivakashi"</string>
+ <!-- no translation found for guest_exit_guest_dialog_title (7587460301980067285) -->
+ <skip />
+ <!-- no translation found for guest_exit_guest_dialog_message (10255285459589280) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_title (6419439912885956132) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_message (5369763062345463297) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_wipe (9154291314115781448) -->
+ <skip />
+ <!-- no translation found for guest_wipe_session_dontwipe (850084868661344050) -->
+ <skip />
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Iminithi elilodwa"</item>
<item quantity="other" msgid="6924190729213550991">"Amaminithi angu-%d"</item>
@@ -281,7 +295,6 @@
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Vula izilungiselelo zesilondolozi sebhethri"</string>
<string name="battery_level_template" msgid="1609636980292580020">"<xliff:g id="LEVEL">%d</xliff:g>%%"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Okuqukethwe kufihliwe"</string>
- <string name="guest_exit_guest" msgid="1619100760451149682">"Phuma kusivakashi"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> izoqala ukuthwebula yonke into eboniswa kusikrini sakho."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ungabonisi futhi"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Qala manje"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 627f11c..e9fe09e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -23,6 +23,7 @@
<color name="system_bar_background_opaque">#ff000000</color>
<color name="system_bar_background_semi_transparent">#66000000</color> <!-- 40% black -->
<color name="system_bar_background_transparent">#00000000</color>
+ <color name="system_bar_background_warning">#fff4511e</color><!-- deep orange 600 -->
<color name="notification_panel_solid_background">#ff000000</color>
<drawable name="status_bar_recents_app_thumbnail_background">#88000000</drawable>
<color name="status_bar_recents_app_label_color">#ffffffff</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 94fcc23..230f4af 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -159,10 +159,19 @@
<!-- Set to true to enable the user switcher on the keyguard. -->
<bool name="config_keyguardUserSwitcher">false</bool>
+ <!-- Doze: does this device support STATE_DOZE and STATE_DOZE_SUSPEND? -->
+ <bool name="doze_display_state_supported">false</bool>
+
<!-- Doze: should the significant motion sensor be used as a tease signal? -->
- <bool name="doze_tease_on_significant_motion">true</bool>
+ <bool name="doze_tease_on_significant_motion">false</bool>
+
+ <!-- Doze: maximum brightness to use when teasing -->
+ <integer name="doze_tease_brightness">80</integer>
<!-- Volume: time to delay dismissing the volume panel after a click is performed -->
<integer name="volume_panel_dismiss_delay">200</integer>
+
+ <!-- Hotspot tile: number of days to show after feature is used. -->
+ <integer name="days_to_show_hotspot">30</integer>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index bb18120..4e536e4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -213,6 +213,9 @@
<!-- The height of the lock-to-app button. -->
<dimen name="recents_task_view_lock_to_app_button_height">48dp</dimen>
+ <!-- The amount to offset when animating into an affiliate group. -->
+ <dimen name="recents_task_view_affiliate_group_enter_offset">64dp</dimen>
+
<!-- The height of a task view bar. -->
<dimen name="recents_task_bar_height">56dp</dimen>
@@ -292,6 +295,15 @@
<!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
<dimen name="keyguard_min_swipe_amount">75dp</dimen>
+ <!-- The minimum background radius when swiping to a side for the camera / phone affordances. -->
+ <dimen name="keyguard_affordance_min_background_radius">30dp</dimen>
+
+ <!-- The grow amount for the camera and phone circles when hinting -->
+ <dimen name="hint_grow_amount_sideways">60dp</dimen>
+
+ <!-- The chevron padding to the circle when hinting -->
+ <dimen name="hint_chevron_circle_padding">16dp</dimen>
+
<!-- Volume panel dialog y offset -->
<dimen name="volume_panel_top">0dp</dimen>
@@ -314,9 +326,6 @@
<!-- Move distance for the unlock hint animation on the lockscreen -->
<dimen name="hint_move_distance">75dp</dimen>
- <!-- Move distance for the other hint animations on the lockscreen (phone, camera)-->
- <dimen name="hint_move_distance_sideways">60dp</dimen>
-
<!-- The width of the region on the left/right edge of the screen for performing the camera/
phone hints. -->
<dimen name="edge_tap_area_width">48dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 46057af..416cd54 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -191,7 +191,7 @@
<!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
<string name="screenshot_failed_title">Couldn\'t capture screenshot.</string>
<!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
- <string name="screenshot_failed_text">Couldn\'t save screenshot. Storage may be in use.</string>
+ <string name="screenshot_failed_text">Can\'t take screenshot due to limited storage space, or it isn\'t allowed by the app or your organization.</string>
<!-- Title for the USB function chooser in UsbPreferenceActivity. [CHAR LIMIT=30] -->
<string name="usb_preference_title">USB file transfer options</string>
@@ -671,12 +671,34 @@
<string name="keyguard_indication_charging_time">Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
<!-- Related to user switcher --><skip/>
- <!-- Name for the guest user -->
+ <!-- Name for the guest user [CHAR LIMIT=35] -->
<string name="guest_nickname">Guest</string>
- <!-- Label for adding a new guest -->
+ <!-- Label for adding a new guest in the user switcher [CHAR LIMIT=35] -->
<string name="guest_new_guest">+ Guest</string>
+ <!-- Label for exiting guest session in the user switcher [CHAR LIMIT=35] -->
+ <string name="guest_exit_guest">Exit guest</string>
+
+ <!-- Title of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
+ <string name="guest_exit_guest_dialog_title">Exiting guest session?</string>
+
+ <!-- Message of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
+ <string name="guest_exit_guest_dialog_message">Exiting the guest session will remove local data.</string>
+
+ <!-- Title of the notification when resuming an existing guest session [CHAR LIMIT=NONE] -->
+ <string name="guest_wipe_session_title">Welcome back, guest!</string>
+
+ <!-- Message of the notification when resuming an existing guest session [CHAR LIMIT=NONE] -->
+ <string name="guest_wipe_session_message">Do you want to start a new session?</string>
+
+ <!-- Notification when resuming an existing guest session: Action that starts a new session [CHAR LIMIT=35] -->
+ <string name="guest_wipe_session_wipe">Yes</string>
+
+ <!-- Notification when resuming an existing guest session: Action that continues with the current session [CHAR LIMIT=35] -->
+ <string name="guest_wipe_session_dontwipe">No, thanks</string>
+
+
<!-- Zen mode condition: time duration in minutes. [CHAR LIMIT=NONE] -->
<plurals name="zen_mode_duration_minutes">
<item quantity="one">For one minute</item>
@@ -704,8 +726,6 @@
<!-- Text shown in place of notification contents when the notification is hidden on a secure lockscreen -->
<string name="notification_hidden_text">Contents hidden</string>
- <string name="guest_exit_guest">Exit guest</string>
-
<!-- Media projection permission dialog warning text. [CHAR LIMIT=NONE] -->
<string name="media_projection_dialog_text"><xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> will start capturing everything that\'s displayed on your screen.</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index abf9337..0b8f876 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -275,4 +275,7 @@
<item name="android:textStyle">italic</item>
<item name="android:textColor">#60000000</item>
</style>
+
+ <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog">
+ </style>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
new file mode 100644
index 0000000..7f5ed6a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui;
+
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+import android.app.ActivityManagerNative;
+import android.app.Dialog;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.WindowManagerGlobal;
+
+/**
+ * Manages notification when a guest session is resumed.
+ */
+public class GuestResumeSessionReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "GuestResumeSessionReceiver";
+
+ private static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
+
+ private Dialog mNewSessionDialog;
+
+ public void register(Context context) {
+ IntentFilter f = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+ context.registerReceiverAsUser(this, UserHandle.OWNER,
+ f, null /* permission */, null /* scheduler */);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ cancelDialog();
+
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ Log.e(TAG, intent + " sent to " + TAG + " without EXTRA_USER_HANDLE");
+ return;
+ }
+
+ UserInfo currentUser;
+ try {
+ currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+ } catch (RemoteException e) {
+ return;
+ }
+ if (!currentUser.isGuest()) {
+ return;
+ }
+
+ ContentResolver cr = context.getContentResolver();
+ int notFirstLogin = Settings.System.getIntForUser(
+ cr, SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
+ if (notFirstLogin != 0) {
+ mNewSessionDialog = new ResetSessionDialog(context, userId);
+ mNewSessionDialog.show();
+ } else {
+ Settings.System.putIntForUser(
+ cr, SETTING_GUEST_HAS_LOGGED_IN, 1, userId);
+ }
+ }
+ }
+
+ /**
+ * Wipes the guest session.
+ *
+ * The guest must be the current user and its id must be {@param userId}.
+ */
+ private static void wipeGuestSession(Context context, int userId) {
+ UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ UserInfo currentUser;
+ try {
+ currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't wipe session because ActivityManager is dead");
+ return;
+ }
+ if (currentUser.id != userId) {
+ Log.w(TAG, "User requesting to start a new session (" + userId + ")"
+ + " is not current user (" + currentUser.id + ")");
+ return;
+ }
+ if (!currentUser.isGuest()) {
+ Log.w(TAG, "User requesting to start a new session (" + userId + ")"
+ + " is not a guest");
+ return;
+ }
+
+ userManager.removeUser(currentUser.id);
+ UserInfo newGuest = userManager.createGuest(context, currentUser.name);
+
+ try {
+ if (newGuest == null) {
+ Log.e(TAG, "Could not create new guest, switching back to owner");
+ ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER);
+ WindowManagerGlobal.getWindowManagerService().lockNow(null /* options */);
+ return;
+ }
+ ActivityManagerNative.getDefault().switchUser(newGuest.id);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't wipe session because ActivityManager or WindowManager is dead");
+ return;
+ }
+ }
+
+ private void cancelDialog() {
+ if (mNewSessionDialog != null && mNewSessionDialog.isShowing()) {
+ mNewSessionDialog.cancel();
+ mNewSessionDialog = null;
+ }
+ }
+
+ private static class ResetSessionDialog extends SystemUIDialog implements
+ DialogInterface.OnClickListener {
+
+ private static final int BUTTON_WIPE = BUTTON_NEGATIVE;
+ private static final int BUTTON_DONTWIPE = BUTTON_POSITIVE;
+
+ private final int mUserId;
+
+ public ResetSessionDialog(Context context, int userId) {
+ super(context);
+
+ setTitle(context.getString(R.string.guest_wipe_session_title));
+ setMessage(context.getString(R.string.guest_wipe_session_message));
+ setCanceledOnTouchOutside(false);
+
+ setButton(BUTTON_WIPE,
+ context.getString(R.string.guest_wipe_session_wipe), this);
+ setButton(BUTTON_DONTWIPE,
+ context.getString(R.string.guest_wipe_session_dontwipe), this);
+
+ mUserId = userId;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == BUTTON_WIPE) {
+ wipeGuestSession(getContext(), mUserId);
+ dismiss();
+ } else if (which == BUTTON_DONTWIPE) {
+ cancel();
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 630ba13..b3f90d7 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -46,8 +46,7 @@
com.android.systemui.statusbar.SystemBars.class,
com.android.systemui.usb.StorageNotification.class,
com.android.systemui.power.PowerUI.class,
- com.android.systemui.media.RingtonePlayer.class,
- com.android.systemui.settings.SettingsUI.class,
+ com.android.systemui.media.RingtonePlayer.class
};
/**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 269b4ed..333b8b4 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -16,20 +16,27 @@
package com.android.systemui.doze;
+import static android.os.PowerManager.BRIGHTNESS_OFF;
+import static android.os.PowerManager.BRIGHTNESS_ON;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
+import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemProperties;
import android.os.Vibrator;
import android.service.dreams.DozeHardware;
import android.service.dreams.DreamService;
import android.util.Log;
+import android.util.MathUtils;
+import android.view.Display;
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
@@ -38,12 +45,14 @@
import java.io.PrintWriter;
public class DozeService extends DreamService {
- private static final boolean DEBUG = false;
+ private static final String TAG = "DozeService";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String TEASE_ACTION = "com.android.systemui.doze.tease";
- private final String mTag = String.format("DozeService.%08x", hashCode());
+ private final String mTag = String.format(TAG + ".%08x", hashCode());
private final Context mContext = this;
+ private final Handler mHandler = new Handler();
private Host mHost;
private DozeHardware mDozeHardware;
@@ -51,10 +60,13 @@
private Sensor mSigMotionSensor;
private PowerManager mPowerManager;
private PowerManager.WakeLock mWakeLock;
+ private int mMaxBrightness;
private boolean mDreaming;
private boolean mTeaseReceiverRegistered;
private boolean mSigMotionConfigured;
private boolean mSigMotionEnabled;
+ private boolean mDisplayStateSupported;
+ private int mDisplayStateWhenOn;
public DozeService() {
if (DEBUG) Log.d(mTag, "new DozeService()");
@@ -70,6 +82,8 @@
pw.print(" mSigMotionSensor: "); pw.println(mSigMotionSensor);
pw.print(" mSigMotionConfigured: "); pw.println(mSigMotionConfigured);
pw.print(" mSigMotionEnabled: "); pw.println(mSigMotionEnabled);
+ pw.print(" mMaxBrightness: "); pw.println(mMaxBrightness);
+ pw.print(" mDisplayStateSupported: "); pw.println(mDisplayStateSupported);
}
@Override
@@ -88,8 +102,16 @@
mSigMotionSensor = mSensors.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
+ final Resources res = mContext.getResources();
mSigMotionConfigured = SystemProperties.getBoolean("doze.tease.sigmotion",
- mContext.getResources().getBoolean(R.bool.doze_tease_on_significant_motion));
+ res.getBoolean(R.bool.doze_tease_on_significant_motion));
+ mDisplayStateSupported = SystemProperties.getBoolean("doze.display.supported",
+ res.getBoolean(R.bool.doze_display_state_supported));
+ mMaxBrightness = MathUtils.constrain(res.getInteger(R.integer.doze_tease_brightness),
+ BRIGHTNESS_OFF, BRIGHTNESS_ON);
+
+ mDisplayStateWhenOn = mDisplayStateSupported ? Display.STATE_DOZE : Display.STATE_ON;
+ setDozeScreenState(mDisplayStateWhenOn);
}
@Override
@@ -112,9 +134,18 @@
public void stayAwake(long millis) {
if (mDreaming && millis > 0) {
mWakeLock.acquire(millis);
+ setDozeScreenState(mDisplayStateWhenOn);
+ setDozeScreenBrightness(mMaxBrightness);
+ rescheduleOff(millis);
}
}
+ private void rescheduleOff(long millis) {
+ if (DEBUG) Log.d(TAG, "rescheduleOff millis=" + millis);
+ mHandler.removeCallbacks(mDisplayOff);
+ mHandler.postDelayed(mDisplayOff, millis);
+ }
+
public void startDozing() {
if (DEBUG) Log.d(mTag, "startDozing mDreaming=" + mDreaming);
if (!mDreaming) {
@@ -225,6 +256,15 @@
return sb.append(']').toString();
}
+ private final Runnable mDisplayOff = new Runnable() {
+ @Override
+ public void run() {
+ if (DEBUG) Log.d(TAG, "Display off");
+ setDozeScreenState(Display.STATE_OFF);
+ setDozeScreenBrightness(PowerManager.BRIGHTNESS_DEFAULT);
+ }
+ };
+
private final TriggerEventListener mSigMotionListener = new TriggerEventListener() {
@Override
public void onTrigger(TriggerEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index fc08cf4..93766af 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -30,11 +30,12 @@
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.Listenable;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.TetheringController;
+import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.volume.VolumeComponent;
@@ -244,16 +245,16 @@
void collapsePanels();
Looper getLooper();
Context getContext();
+ List<QSTile<?>> getTiles();
BluetoothController getBluetoothController();
LocationController getLocationController();
RotationLockController getRotationLockController();
- List<QSTile<?>> getTiles();
NetworkController getNetworkController();
ZenModeController getZenModeController();
- TetheringController getTetheringController();
+ HotspotController getHotspotController();
CastController getCastController();
- VolumeComponent getVolumeComponent();
FlashlightController getFlashlightController();
+ KeyguardMonitor getKeyguardMonitor();
}
public static class State {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index ab952c6..531ac31 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -30,6 +30,7 @@
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
import java.util.LinkedHashMap;
import java.util.Set;
@@ -41,11 +42,14 @@
private final CastController mController;
private final CastDetailAdapter mDetailAdapter;
+ private final KeyguardMonitor mKeyguard;
+ private final Callback mCallback = new Callback();
public CastTile(Host host) {
super(host);
mController = host.getCastController();
mDetailAdapter = new CastDetailAdapter();
+ mKeyguard = host.getKeyguardMonitor();
}
@Override
@@ -64,9 +68,11 @@
if (DEBUG) Log.d(TAG, "setListening " + listening);
if (listening) {
mController.addCallback(mCallback);
+ mKeyguard.addCallback(mCallback);
} else {
mController.setDiscovering(false);
mController.removeCallback(mCallback);
+ mKeyguard.removeCallback(mCallback);
}
}
@@ -84,7 +90,7 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- state.visible = true;
+ state.visible = !(mKeyguard.isSecure() && mKeyguard.isShowing());
state.label = mContext.getString(R.string.quick_settings_cast_title);
state.value = false;
final Set<CastDevice> devices = mController.getCastDevices();
@@ -109,11 +115,16 @@
: mContext.getString(R.string.quick_settings_cast_device_default_name);
}
- private final CastController.Callback mCallback = new CastController.Callback() {
+ private final class Callback implements CastController.Callback, KeyguardMonitor.Callback {
@Override
public void onCastDevicesChanged() {
refreshState();
}
+
+ @Override
+ public void onKeyguardChanged() {
+ refreshState();
+ }
};
private final class CastDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index b610cf3..75e3bc1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -62,7 +62,9 @@
if (arg instanceof Boolean) {
state.value = (Boolean) arg;
}
- state.visible = mFlashlightController.isAvailable();
+ // Always show the tile when the flashlight is on. This is needed because
+ // the camera is not available while it is being used for the flashlight.
+ state.visible = state.value || mFlashlightController.isAvailable();
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
state.iconId = state.value
? R.drawable.ic_qs_flashlight_on : R.drawable.ic_qs_flashlight_off;
@@ -77,4 +79,9 @@
public void onFlashlightError() {
refreshState(false);
}
+
+ @Override
+ public void onFlashlightAvailabilityChanged(boolean available) {
+ refreshState();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index f2ba558..35dfdda 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -16,45 +16,98 @@
package com.android.systemui.qs.tiles;
+import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
-import com.android.systemui.statusbar.policy.TetheringController;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
/** Quick settings tile: Hotspot **/
-public class HotspotTile extends QSTile<QSTile.State> {
- private static final Intent TETHER_SETTINGS = new Intent()
- .setClassName("com.android.settings", "com.android.settings.TetherSettings");
+public class HotspotTile extends QSTile<QSTile.BooleanState> {
+ private static final String KEY_LAST_USED_DATE = "lastUsedDate";
+ private static final long MILLIS_PER_DAY = 1000 * 60 * 60 * 24;
- // TODO: implement. see com.android.settings.TetherSettings
-
- private final TetheringController mController;
+ private final HotspotController mController;
+ private final KeyguardMonitor mKeyguard;
+ private final Callback mCallback = new Callback();
+ private final long mTimeToShowTile;
public HotspotTile(Host host) {
super(host);
- mController = host.getTetheringController();
+ mController = host.getHotspotController();
+ mKeyguard = host.getKeyguardMonitor();
+
+ mTimeToShowTile = MILLIS_PER_DAY
+ * mContext.getResources().getInteger(R.integer.days_to_show_hotspot);
}
@Override
- protected State newTileState() {
- return new State();
+ protected BooleanState newTileState() {
+ return new BooleanState();
}
@Override
public void setListening(boolean listening) {
-
+ if (listening) {
+ mController.addCallback(mCallback);
+ mKeyguard.addCallback(mCallback);
+ } else {
+ mController.removeCallback(mCallback);
+ mKeyguard.removeCallback(mCallback);
+ }
}
@Override
protected void handleClick() {
- mHost.startSettingsActivity(TETHER_SETTINGS);
+ final boolean isEnabled = (Boolean) mState.value;
+ mController.setHotspotEnabled(!isEnabled);
}
@Override
- protected void handleUpdateState(State state, Object arg) {
- state.visible = mController != null;
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ state.visible = !(mKeyguard.isSecure() && mKeyguard.isShowing())
+ && mController.isHotspotSupported() && isHotspotRecentlyUsed();
state.label = mContext.getString(R.string.quick_settings_hotspot_label);
- state.iconId = R.drawable.ic_qs_hotspot_off;
+
+ state.value = mController.isHotspotEnabled();
+ state.iconId = state.visible && state.value ? R.drawable.ic_qs_hotspot_on
+ : R.drawable.ic_qs_hotspot_off;
+ }
+
+ private boolean isHotspotRecentlyUsed() {
+ long lastDay = getSharedPrefs(mContext).getLong(KEY_LAST_USED_DATE, 0);
+ return (System.currentTimeMillis() - lastDay) < mTimeToShowTile;
+ }
+
+ private static SharedPreferences getSharedPrefs(Context context) {
+ return context.getSharedPreferences(context.getPackageName(), 0);
+ }
+
+ private final class Callback implements HotspotController.Callback, KeyguardMonitor.Callback {
+ @Override
+ public void onHotspotChanged(boolean enabled) {
+ refreshState();
+ }
+
+ @Override
+ public void onKeyguardChanged() {
+ refreshState();
+ }
+ };
+
+ /**
+ * This will catch broadcasts for changes in hotspot state so we can show
+ * the hotspot tile for a number of days after use.
+ */
+ public static class APChangedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ long currentTime = System.currentTimeMillis();
+ getSharedPrefs(context).edit().putLong(KEY_LAST_USED_DATE, currentTime).commit();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 04f1eb5..c4dd643 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -18,6 +18,7 @@
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
@@ -25,10 +26,13 @@
public class LocationTile extends QSTile<QSTile.BooleanState> {
private final LocationController mController;
+ private final KeyguardMonitor mKeyguard;
+ private final Callback mCallback = new Callback();
public LocationTile(Host host) {
super(host);
mController = host.getLocationController();
+ mKeyguard = host.getKeyguardMonitor();
}
@Override
@@ -40,8 +44,10 @@
public void setListening(boolean listening) {
if (listening) {
mController.addSettingsChangedCallback(mCallback);
+ mKeyguard.addCallback(mCallback);
} else {
mController.removeSettingsChangedCallback(mCallback);
+ mKeyguard.removeCallback(mCallback);
}
}
@@ -59,7 +65,7 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
final boolean locationEnabled = mController.isLocationEnabled();
- state.visible = true;
+ state.visible = !(mKeyguard.isSecure() && mKeyguard.isShowing());
state.value = locationEnabled;
if (locationEnabled) {
state.iconId = R.drawable.ic_qs_location_on;
@@ -76,10 +82,16 @@
}
}
- private final LocationSettingsChangeCallback mCallback = new LocationSettingsChangeCallback() {
+ private final class Callback implements LocationSettingsChangeCallback,
+ KeyguardMonitor.Callback {
@Override
public void onLocationSettingsChanged(boolean enabled) {
refreshState();
}
+
+ @Override
+ public void onKeyguardChanged() {
+ refreshState();
+ }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 66e5d14..591149c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -70,10 +71,13 @@
// Task launching
RecentsConfiguration mConfig;
- Rect mWindowRect;
- Rect mTaskStackBounds;
+ Rect mWindowRect = new Rect();
+ Rect mTaskStackBounds = new Rect();
+ Rect mSystemInsets = new Rect();
TaskViewTransform mTmpTransform = new TaskViewTransform();
int mStatusBarHeight;
+ int mNavBarHeight;
+ int mNavBarWidth;
// Variables to keep track of if we need to start recents after binding
View mStatusBarView;
@@ -81,15 +85,23 @@
long mLastToggleTime;
public AlternateRecentsComponent(Context context) {
+ Resources res = context.getResources();
mContext = context;
mSystemServicesProxy = new SystemServicesProxy(context);
mHandler = new Handler();
mConfig = RecentsConfiguration.reinitialize(context, mSystemServicesProxy);
mWindowRect = mSystemServicesProxy.getWindowRect();
mTaskStackBounds = new Rect();
- mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mTaskStackBounds);
- mStatusBarHeight = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
+ mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+ mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
+ mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
+ mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mStatusBarHeight,
+ mNavBarWidth, mTaskStackBounds);
+ if (mConfig.isLandscape && mConfig.transposeRecentsLayoutWithOrientation) {
+ mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
+ } else {
+ mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
+ }
}
public void onStart() {
@@ -150,7 +162,13 @@
mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
mConfig.updateOnConfigurationChange();
mWindowRect = mSystemServicesProxy.getWindowRect();
- mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mTaskStackBounds);
+ mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mStatusBarHeight,
+ mNavBarWidth, mTaskStackBounds);
+ if (mConfig.isLandscape && mConfig.transposeRecentsLayoutWithOrientation) {
+ mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
+ } else {
+ mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
+ }
sLastScreenshot = null;
}
@@ -301,7 +319,9 @@
// Get the stack
TaskStackView tsv = new TaskStackView(mContext, stack);
TaskStackViewLayoutAlgorithm algo = tsv.getStackAlgorithm();
- tsv.computeRects(mTaskStackBounds.width(), mTaskStackBounds.height() - mStatusBarHeight, 0, 0);
+ Rect taskStackBounds = new Rect(mTaskStackBounds);
+ taskStackBounds.bottom -= mSystemInsets.bottom;
+ tsv.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
tsv.setStackScrollToInitialState();
// Find the running task in the TaskStack
@@ -325,8 +345,6 @@
// Get the transform for the running task
mTmpTransform = algo.getStackTransform(task, tsv.getStackScroll(), mTmpTransform);
- mTmpTransform.rect.offset(mTaskStackBounds.left, mTaskStackBounds.top);
- mTmpTransform.rect.offset(0, mStatusBarHeight);
return new Rect(mTmpTransform.rect);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 5741f22..18dad3a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -32,18 +32,20 @@
import android.util.Pair;
import android.view.KeyEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewStub;
+import android.widget.FrameLayout;
import android.widget.Toast;
import com.android.systemui.R;
-import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.misc.DebugTrigger;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.SpaceNode;
+import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.FullscreenTransitionOverlayView;
+import com.android.systemui.recents.views.DebugOverlayView;
import com.android.systemui.recents.views.RecentsView;
import com.android.systemui.recents.views.SystemBarScrimViews;
import com.android.systemui.recents.views.ViewAnimation;
@@ -56,8 +58,7 @@
* The main Recents activity that is started from AlternateRecentsComponent.
*/
public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
- RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
- FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks {
+ RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks {
// Actions and Extras sent from AlternateRecentsComponent
final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab";
@@ -73,17 +74,14 @@
SystemBarScrimViews mScrimViews;
ViewStub mEmptyViewStub;
View mEmptyView;
- ViewStub mFullscreenOverlayStub;
- FullscreenTransitionOverlayView mFullScreenOverlayView;
+ DebugOverlayView mDebugOverlay;
// Search AppWidget
RecentsAppWidgetHost mAppWidgetHost;
AppWidgetProviderInfo mSearchAppWidgetInfo;
AppWidgetHostView mSearchAppWidgetHostView;
-
// Runnables to finish the Recents activity
- FinishRecentsRunnable mFinishRunnable = new FinishRecentsRunnable();
FinishRecentsRunnable mFinishLaunchHomeRunnable;
/**
@@ -97,10 +95,6 @@
Intent mLaunchIntent;
ActivityOptions mLaunchOpts;
- public FinishRecentsRunnable() {
- // Do nothing
- }
-
/**
* Creates a finish runnable that starts the specified intent, using the given
* ActivityOptions.
@@ -151,8 +145,7 @@
} else if (action.equals(ACTION_START_ENTER_ANIMATION)) {
// Try and start the enter animation (or restart it on configuration changed)
ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null);
- mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(
- mFullScreenOverlayView, t));
+ mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t));
onEnterAnimationTriggered();
}
}
@@ -187,11 +180,12 @@
/** Updates the set of recent tasks */
void updateRecentsTasks(Intent launchIntent) {
+ // Load all the tasks
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
ArrayList<TaskStack> stacks = root.getStacks();
if (!stacks.isEmpty()) {
- mRecentsView.setBSP(root);
+ mRecentsView.setTaskStacks(root.getStacks());
}
// Update the configuration based on the launch intent
@@ -207,6 +201,23 @@
mConfig.launchedToTaskId = launchIntent.getIntExtra(
AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_TASK_ID, -1);
+ // Mark the task that is the launch target
+ int taskStackCount = stacks.size();
+ if (mConfig.launchedToTaskId != -1) {
+ for (int i = 0; i < taskStackCount; i++) {
+ TaskStack stack = stacks.get(i);
+ ArrayList<Task> tasks = stack.getTasks();
+ int taskCount = tasks.size();
+ for (int j = 0; j < taskCount; j++) {
+ Task t = tasks.get(j);
+ if (t.key.id == mConfig.launchedToTaskId) {
+ t.isLaunchTarget = true;
+ break;
+ }
+ }
+ }
+ }
+
// Update the top level view's visibilities
if (mConfig.launchedWithNoRecentTasks) {
if (mEmptyView == null) {
@@ -286,9 +297,6 @@
/** Dismisses recents if we are already visible and the intent is to toggle the recents view */
boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
if (mVisible) {
- // If we are mid-animation into Recents, reverse the animation now
- if (mFullScreenOverlayView != null &&
- mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) return true;
// If we currently have filtered stacks, then unfilter those first
if (checkFilteredStackState &&
mRecentsView.unfilterFilteredStacks()) return true;
@@ -299,8 +307,8 @@
dismissRecentsToHomeRaw(true);
return true;
}
- // Otherwise, try and return to the first Task in the stack
- if (mRecentsView.launchFirstTask()) return true;
+ // Otherwise, try and return to the Task that Recents was launched from
+ if (mRecentsView.launchPreviousTask()) return true;
// If none of the other cases apply, then just go Home
dismissRecentsToHomeRaw(true);
return true;
@@ -323,9 +331,6 @@
/** Dismisses Recents directly to Home if we currently aren't transitioning. */
boolean dismissRecentsToHome(boolean animated) {
if (mVisible) {
- // If we are mid-animation into Recents, reverse the animation now
- if (mFullScreenOverlayView != null &&
- mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) return true;
// Return to Home
dismissRecentsToHomeRaw(animated);
return true;
@@ -363,8 +368,8 @@
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
- mFullscreenOverlayStub = (ViewStub) findViewById(R.id.fullscreen_overlay_stub);
mScrimViews = new SystemBarScrimViews(this, mConfig);
+ inflateDebugOverlay();
// Bind the search app widget when we first start up
bindSearchBarAppWidget();
@@ -390,13 +395,6 @@
e.printStackTrace();
}
- // Prepare the screenshot transition if necessary
- if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
- mFullScreenOverlayView = (FullscreenTransitionOverlayView) mFullscreenOverlayStub.inflate();
- mFullScreenOverlayView.setCallbacks(this);
- mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
- }
-
// Update if we are getting a configuration change
if (savedInstanceState != null) {
mConfig.updateOnConfigurationChange();
@@ -404,6 +402,19 @@
}
}
+ /** Inflates the debug overlay if debug mode is enabled. */
+ void inflateDebugOverlay() {
+ if (mConfig.debugModeEnabled && mDebugOverlay == null) {
+ ViewGroup parent = (ViewGroup) findViewById(android.R.id.content).getRootView();
+ mDebugOverlay = new DebugOverlayView(this);
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ parent.addView(mDebugOverlay, lp);
+ mRecentsView.setDebugOverlay(mDebugOverlay);
+ }
+ }
+
void onConfigurationChange() {
// Update RecentsConfiguration
mConfig = RecentsConfiguration.reinitialize(this,
@@ -411,8 +422,7 @@
// Try and start the enter animation (or restart it on configuration changed)
ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
- mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(
- mFullScreenOverlayView, t));
+ mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t));
onEnterAnimationTriggered();
}
@@ -421,13 +431,16 @@
super.onNewIntent(intent);
setIntent(intent);
+ // Reinitialize the configuration
+ RecentsConfiguration.reinitialize(this, RecentsTaskLoader.getInstance().getSystemServicesProxy());
+
+ // Clear any debug rects
+ if (mDebugOverlay != null) {
+ mDebugOverlay.clear();
+ }
+
// Update the recent tasks
updateRecentsTasks(intent);
-
- // Prepare the screenshot transition if necessary
- if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
- mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
- }
}
@Override
@@ -531,17 +544,25 @@
/** Called when debug mode is triggered */
public void onDebugModeTriggered() {
+
if (mConfig.developerOptionsEnabled) {
SharedPreferences settings = getSharedPreferences(getPackageName(), 0);
if (settings.getBoolean(Constants.Values.App.Key_DebugModeEnabled, false)) {
// Disable the debug mode
settings.edit().remove(Constants.Values.App.Key_DebugModeEnabled).apply();
+ mConfig.debugModeEnabled = false;
+ inflateDebugOverlay();
+ mDebugOverlay.disable();
} else {
// Enable the debug mode
settings.edit().putBoolean(Constants.Values.App.Key_DebugModeEnabled, true).apply();
+ mConfig.debugModeEnabled = true;
+ inflateDebugOverlay();
+ mDebugOverlay.enable();
}
- Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion +
- ") toggled, please restart Recents now", Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion + ") " +
+ (mConfig.debugModeEnabled ? "Enabled" : "Disabled") + ", please restart Recents now",
+ Toast.LENGTH_SHORT).show();
}
}
@@ -551,19 +572,6 @@
mScrimViews.startEnterRecentsAnimation();
}
- /**** FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks Implementation ****/
-
- @Override
- public void onEnterAnimationComplete() {
- // Reset the full screenshot transition view
- if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
- mFullScreenOverlayView.reset();
-
- // Recycle the full screen screenshot
- AlternateRecentsComponent.consumeLastScreenshot();
- }
- }
-
/**** RecentsView.RecentsViewCallbacks Implementation ****/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index a0cab5c..36d06d5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -80,6 +80,7 @@
public int taskViewTranslationZIncrementPx;
public int taskViewRoundedCornerRadiusPx;
public int taskViewHighlightPx;
+ public int taskViewAffiliateGroupEnterOffsetPx;
/** Task bar colors */
public int taskBarViewDefaultBackgroundColor;
@@ -210,6 +211,8 @@
taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
taskViewTranslationZIncrementPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
+ taskViewAffiliateGroupEnterOffsetPx =
+ res.getDimensionPixelSize(R.dimen.recents_task_view_affiliate_group_enter_offset);
// Task bar colors
taskBarViewDefaultBackgroundColor =
@@ -305,23 +308,25 @@
(!transposeRecentsLayoutWithOrientation || !isLandscape);
}
+ /** Returns whether the current layout is horizontal. */
+ public boolean hasHorizontalLayout() {
+ return isLandscape && transposeRecentsLayoutWithOrientation;
+ }
+
/**
* Returns the task stack bounds in the current orientation. These bounds do not account for
* the system insets.
*/
- public void getTaskStackBounds(int width, int height, Rect taskStackBounds) {
- if (hasSearchBarAppWidget()) {
- Rect searchBarBounds = new Rect();
- getSearchBarBounds(width, height, searchBarBounds);
- if (isLandscape && transposeRecentsLayoutWithOrientation) {
- // In landscape, the search bar appears on the left, so shift the task rect right
- taskStackBounds.set(searchBarBounds.width(), 0, width, height);
- } else {
- // In portrait, the search bar appears on the top, so shift the task rect below
- taskStackBounds.set(0, searchBarBounds.height(), width, height);
- }
+ public void getTaskStackBounds(int windowWidth, int windowHeight, int topInset, int rightInset,
+ Rect taskStackBounds) {
+ Rect searchBarBounds = new Rect();
+ getSearchBarBounds(windowWidth, windowHeight, topInset, searchBarBounds);
+ if (isLandscape && transposeRecentsLayoutWithOrientation) {
+ // In landscape, the search bar appears on the left
+ taskStackBounds.set(searchBarBounds.right, topInset, windowWidth - rightInset, windowHeight);
} else {
- taskStackBounds.set(0, 0, width, height);
+ // In portrait, the search bar appears on the top (which already has the inset)
+ taskStackBounds.set(0, searchBarBounds.bottom, windowWidth, windowHeight);
}
}
@@ -329,19 +334,20 @@
* Returns the search bar bounds in the current orientation. These bounds do not account for
* the system insets.
*/
- public void getSearchBarBounds(int width, int height, Rect searchBarSpaceBounds) {
+ public void getSearchBarBounds(int windowWidth, int windowHeight, int topInset,
+ Rect searchBarSpaceBounds) {
// Return empty rects if search is not enabled
- if (!Constants.DebugFlags.App.EnableSearchLayout) {
- searchBarSpaceBounds.set(0, 0, 0, 0);
- return;
+ int searchBarSize = searchBarSpaceHeightPx;
+ if (!Constants.DebugFlags.App.EnableSearchLayout || !hasSearchBarAppWidget()) {
+ searchBarSize = 0;
}
if (isLandscape && transposeRecentsLayoutWithOrientation) {
// In landscape, the search bar appears on the left
- searchBarSpaceBounds.set(0, 0, searchBarSpaceHeightPx, height);
+ searchBarSpaceBounds.set(0, topInset, searchBarSize, windowHeight);
} else {
// In portrait, the search bar appears on the top
- searchBarSpaceBounds.set(0, 0, width, searchBarSpaceHeightPx);
+ searchBarSpaceBounds.set(0, topInset, windowWidth, topInset + searchBarSize);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 5fadc71..fb77751 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -38,6 +38,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
@@ -437,7 +438,9 @@
Rect windowRect = new Rect();
if (mWm == null) return windowRect;
- mWm.getDefaultDisplay().getRectSize(windowRect);
+ Point p = new Point();
+ mWm.getDefaultDisplay().getRealSize(p);
+ windowRect.set(0, 0, p.x, p.y);
return windowRect;
}
@@ -467,4 +470,13 @@
public Bitmap takeAppScreenshot() {
return takeScreenshot();
}
+
+ public void startActivityFromRecents(int taskId, ActivityOptions options) {
+ if (mIam != null) {
+ try {
+ mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle());
+ } catch (RemoteException e) {
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 4cf9235..41874fc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -77,6 +77,7 @@
public TaskKey key;
public TaskGrouping group;
public int taskAffiliation;
+ public boolean isLaunchTarget;
public Drawable applicationIcon;
public Drawable activityIcon;
public String activityLabel;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
index 9791038..326485e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
@@ -53,6 +53,18 @@
return mTaskKeyIndices.get(t.key);
}
+ /** Returns whether a task is in this grouping. */
+ public boolean containsTask(Task t) {
+ return mTaskKeyIndices.containsKey(t.key);
+ }
+
+ /** Returns whether one task is above another in the group. If they are not in the same group,
+ * this returns false. */
+ public boolean isTaskAboveTask(Task t, Task below) {
+ return mTaskKeyIndices.containsKey(t.key) && mTaskKeyIndices.containsKey(below.key) &&
+ mTaskKeyIndices.get(t.key) > mTaskKeyIndices.get(below.key);
+ }
+
/** Returns the number of tasks in this group. */
public int getTaskCount() { return mTaskKeys.size(); }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
new file mode 100644
index 0000000..34f73c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import com.android.systemui.recents.RecentsConfiguration;
+
+/* An outline provider that has a clip and outline that can be animated. */
+public class AnimateableViewBounds extends ViewOutlineProvider {
+
+ RecentsConfiguration mConfig;
+
+ View mSourceView;
+ Rect mClipRect = new Rect();
+ Rect mOutlineClipRect = new Rect();
+ int mCornerRadius;
+
+ ObjectAnimator mClipTopAnimator;
+ ObjectAnimator mClipRightAnimator;
+ ObjectAnimator mClipBottomAnimator;
+
+ public AnimateableViewBounds(View source, int cornerRadius) {
+ mConfig = RecentsConfiguration.getInstance();
+ mSourceView = source;
+ mCornerRadius = cornerRadius;
+ mSourceView.setClipToOutline(true);
+ setClipTop(getClipTop());
+ setClipRight(getClipRight());
+ setClipBottom(getClipBottom());
+ setOutlineClipBottom(getOutlineClipBottom());
+ }
+
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(Math.max(mClipRect.left, mOutlineClipRect.left),
+ Math.max(mClipRect.top, mOutlineClipRect.top),
+ mSourceView.getMeasuredWidth() - Math.max(mClipRect.right, mOutlineClipRect.right),
+ mSourceView.getMeasuredHeight() - Math.max(mClipRect.bottom, mOutlineClipRect.bottom),
+ mCornerRadius);
+ }
+
+ /** Animates the top clip. */
+ void animateClipTop(int top, int duration, ValueAnimator.AnimatorUpdateListener updateListener) {
+ if (mClipTopAnimator != null) {
+ mClipTopAnimator.removeAllListeners();
+ mClipTopAnimator.cancel();
+ }
+ mClipTopAnimator = ObjectAnimator.ofInt(this, "clipTop", top);
+ mClipTopAnimator.setDuration(duration);
+ mClipTopAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
+ if (updateListener != null) {
+ mClipTopAnimator.addUpdateListener(updateListener);
+ }
+ mClipTopAnimator.start();
+ }
+
+ /** Sets the top clip. */
+ public void setClipTop(int top) {
+ if (top != mClipRect.top) {
+ mClipRect.top = top;
+ mSourceView.invalidateOutline();
+ }
+ }
+
+ /** Returns the top clip. */
+ public int getClipTop() {
+ return mClipRect.top;
+ }
+
+ /** Animates the right clip. */
+ void animateClipRight(int right, int duration) {
+ if (mClipRightAnimator != null) {
+ mClipRightAnimator.removeAllListeners();
+ mClipRightAnimator.cancel();
+ }
+ mClipRightAnimator = ObjectAnimator.ofInt(this, "clipRight", right);
+ mClipRightAnimator.setDuration(duration);
+ mClipRightAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
+ mClipRightAnimator.start();
+ }
+
+ /** Sets the right clip. */
+ public void setClipRight(int right) {
+ if (right != mClipRect.right) {
+ mClipRect.right = right;
+ mSourceView.invalidateOutline();
+ }
+ }
+
+ /** Returns the right clip. */
+ public int getClipRight() {
+ return mClipRect.right;
+ }
+
+ /** Animates the bottom clip. */
+ void animateClipBottom(int bottom, int duration) {
+ if (mClipBottomAnimator != null) {
+ mClipBottomAnimator.removeAllListeners();
+ mClipBottomAnimator.cancel();
+ }
+ mClipBottomAnimator = ObjectAnimator.ofInt(this, "clipBottom", bottom);
+ mClipBottomAnimator.setDuration(duration);
+ mClipBottomAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
+ mClipBottomAnimator.start();
+ }
+
+ /** Sets the bottom clip. */
+ public void setClipBottom(int bottom) {
+ if (bottom != mClipRect.bottom) {
+ mClipRect.bottom = bottom;
+ mSourceView.invalidateOutline();
+ }
+ }
+
+ /** Returns the bottom clip. */
+ public int getClipBottom() {
+ return mClipRect.bottom;
+ }
+
+ /** Sets the outline bottom clip. */
+ public void setOutlineClipBottom(int bottom) {
+ if (bottom != mOutlineClipRect.bottom) {
+ mOutlineClipRect.bottom = bottom;
+ mSourceView.invalidateOutline();
+ }
+ }
+
+ /** Gets the outline bottom clip. */
+ public int getOutlineClipBottom() {
+ return mOutlineClipRect.bottom;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java b/packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java
new file mode 100644
index 0000000..6c90fe3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.Pair;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import java.util.ArrayList;
+
+/**
+ * A full screen overlay layer that allows us to draw views from throughout the system on the top
+ * most layer.
+ */
+public class DebugOverlayView extends FrameLayout {
+
+ final static int sCornerRectSize = 50;
+
+ ArrayList<Pair<Rect, Integer>> mRects = new ArrayList<Pair<Rect, Integer>>();
+ Paint mDebugOutline = new Paint();
+ Paint mTmpPaint = new Paint();
+ boolean mEnabled = true;
+
+ public DebugOverlayView(Context context) {
+ super(context);
+ mDebugOutline.setColor(0xFFff0000);
+ mDebugOutline.setStyle(Paint.Style.STROKE);
+ mDebugOutline.setStrokeWidth(8f);
+ setWillNotDraw(false);
+ }
+
+ /** Enables the debug overlay drawing. */
+ public void enable() {
+ mEnabled = true;
+ invalidate();
+ }
+
+ /** Disables the debug overlay drawing. */
+ public void disable() {
+ mEnabled = false;
+ invalidate();
+ }
+
+ /** Clears all debug rects. */
+ public void clear() {
+ mRects.clear();
+ }
+
+ /** Adds a rect to be drawn. */
+ void addRect(Rect r, int color) {
+ mRects.add(new Pair<Rect, Integer>(r, color));
+ invalidate();
+ }
+
+ /** Adds a view's global rect to be drawn. */
+ void addViewRect(View v, int color) {
+ Rect vr = new Rect();
+ v.getGlobalVisibleRect(vr);
+ mRects.add(new Pair<Rect, Integer>(vr, color));
+ invalidate();
+ }
+
+ /** Adds a rect, relative to a given view to be drawn. */
+ void addRectRelativeToView(View v, Rect r, int color) {
+ Rect vr = new Rect();
+ v.getGlobalVisibleRect(vr);
+ r.offsetTo(vr.left, vr.top);
+ mRects.add(new Pair<Rect, Integer>(r, color));
+ invalidate();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ addRect(new Rect(0, 0, sCornerRectSize, sCornerRectSize), 0xFFff0000);
+ addRect(new Rect(getMeasuredWidth() - sCornerRectSize, getMeasuredHeight() - sCornerRectSize,
+ getMeasuredWidth(), getMeasuredHeight()), 0xFFff0000);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mEnabled) {
+ // Draw the outline
+ canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mDebugOutline);
+
+ // Draw the rects
+ int numRects = mRects.size();
+ for (int i = 0; i < numRects; i++) {
+ Pair<Rect, Integer> r = mRects.get(i);
+ mTmpPaint.setColor(r.second);
+ canvas.drawRect(r.first, mTmpPaint);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
index 3adee0ea..4b5c0bd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents.views;
import android.content.Context;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
@@ -27,8 +28,6 @@
*/
public class FixedSizeImageView extends ImageView {
- int mFixedWidth;
- int mFixedHeight;
boolean mAllowRelayout = true;
boolean mAllowInvalidate = true;
@@ -49,13 +48,6 @@
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- mFixedWidth = getMeasuredWidth();
- mFixedHeight = getMeasuredHeight();
- }
-
- @Override
public void requestLayout() {
if (mAllowRelayout) {
super.requestLayout();
@@ -71,7 +63,9 @@
@Override
public void setImageDrawable(Drawable drawable) {
- if (drawable == null || (mFixedWidth > 0 && mFixedHeight > 0)) {
+ boolean isNullBitmapDrawable = (drawable instanceof BitmapDrawable) &&
+ (((BitmapDrawable) drawable).getBitmap() == null);
+ if (drawable == null || isNullBitmapDrawable) {
mAllowRelayout = false;
mAllowInvalidate = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
deleted file mode 100644
index 63f59be..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.animation.AccelerateInterpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import com.android.systemui.R;
-import com.android.systemui.recents.RecentsConfiguration;
-
-
-/**
- * The full screen transition view that gets animated down from the full screen into a task
- * thumbnail view.
- */
-public class FullscreenTransitionOverlayView extends FrameLayout {
-
- /** The FullscreenTransitionOverlayView callbacks */
- public interface FullScreenTransitionViewCallbacks {
- void onEnterAnimationComplete();
- }
-
- RecentsConfiguration mConfig;
-
- FullScreenTransitionViewCallbacks mCb;
-
- ImageView mScreenshotView;
- Rect mClipRect = new Rect();
- Paint mLayerPaint = new Paint();
- PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.MULTIPLY);
-
- int mDim;
- int mMaxDim;
- AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
-
- boolean mIsAnimating;
- AnimatorSet mEnterAnimation;
-
- public FullscreenTransitionOverlayView(Context context) {
- super(context);
- }
-
- public FullscreenTransitionOverlayView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public FullscreenTransitionOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public FullscreenTransitionOverlayView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- mConfig = RecentsConfiguration.getInstance();
- mMaxDim = mConfig.taskStackMaxDim;
- setClipTop(getClipTop());
- setClipBottom(getClipBottom());
- setDim(getDim());
- setWillNotDraw(false);
- }
-
- @Override
- protected void onFinishInflate() {
- mScreenshotView = (ImageView) findViewById(R.id.image);
- }
-
- /** Sets the callbacks */
- public void setCallbacks(FullScreenTransitionViewCallbacks cb) {
- mCb = cb;
- }
-
- /** Sets the top clip */
- public void setClipTop(int clip) {
- mClipRect.top = clip;
- setClipBounds(mClipRect);
- }
-
- /** Gets the top clip */
- public int getClipTop() {
- return mClipRect.top;
- }
-
- /** Sets the bottom clip */
- public void setClipBottom(int clip) {
- mClipRect.bottom = clip;
- setClipBounds(mClipRect);
- }
-
- /** Gets the top clip */
- public int getClipBottom() {
- return mClipRect.bottom;
- }
-
- /** Returns the current dim. */
- public void setDim(int dim) {
- mDim = dim;
- /*
- int inverse = 255 - mDim;
- mDimColorFilter.setColor(Color.argb(0xFF, inverse, inverse, inverse));
- mLayerPaint.setColorFilter(mDimColorFilter);
- setLayerType(LAYER_TYPE_HARDWARE, mLayerPaint);
- */
- }
-
- /** Returns the current dim. */
- public int getDim() {
- return mDim;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- mClipRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-
- /** Prepares the screenshot view for the transition into Recents */
- public void prepareAnimateOnEnterRecents(Bitmap screenshot) {
- if (!mConfig.launchedFromAppWithScreenshot) return;
-
- setClipTop(0);
- setClipBottom(getMeasuredHeight());
- setDim(0);
- setTranslationY(0f);
- setScaleX(1f);
- setScaleY(1f);
- setVisibility(mConfig.launchedFromAppWithScreenshot ? View.VISIBLE : View.INVISIBLE);
- if (screenshot != null) {
- mScreenshotView.setImageBitmap(screenshot);
- } else {
- mScreenshotView.setImageDrawable(null);
- }
- }
-
- /** Resets the transition view */
- public void reset() {
- setVisibility(View.GONE);
- mScreenshotView.setImageDrawable(null);
- }
-
- /** Animates this view as it enters recents */
- public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx,
- final Runnable postAnimRunnable) {
- // Cancel the current animation
- if (mEnterAnimation != null) {
- mEnterAnimation.removeAllListeners();
- mEnterAnimation.cancel();
- }
-
- // Calculate the bottom clip
- Rect targetTaskRect = ctx.targetTaskTransform.rect;
- float scale = (float) targetTaskRect.width() / getMeasuredWidth();
- float scaleYOffset = ((1f - scale) * getMeasuredHeight()) / 2;
- float scaledTopInset = (int) (scale * mConfig.systemInsets.top);
- int translationY = (int) -scaleYOffset + (int) (mConfig.systemInsets.top - scaledTopInset)
- + targetTaskRect.top;
- int clipBottom = mConfig.systemInsets.top + (int) (targetTaskRect.height() / scale);
-
- // Calculate the dim
- float minScale = TaskStackViewLayoutAlgorithm.StackPeekMinScale;
- float scaleRange = 1f - minScale;
- float dim = (1f - ctx.targetTaskTransform.scale) / scaleRange;
- dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
- int toDim = Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
-
- // Enable the HW Layers on the screenshot view
- mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
-
- // Compose the animation
- mEnterAnimation = new AnimatorSet();
- mEnterAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- post(new Runnable() {
- @Override
- public void run() {
- // Mark that we are no longer animating
- mIsAnimating = false;
- // Disable the HW Layers on this view
- setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
- // Notify any callbacks
- mCb.onEnterAnimationComplete();
- // Run the given post-anim runnable
- postAnimRunnable.run();
- }
- });
- }
- });
- // XXX: Translation y should be negative initially to simulate moving from the top of the screen?
- mEnterAnimation.setStartDelay(0);
- mEnterAnimation.setDuration(475);
- mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator);
- mEnterAnimation.playTogether(
- // ObjectAnimator.ofInt(this, "clipTop", mConfig.systemInsets.top),
- ObjectAnimator.ofInt(this, "clipBottom", clipBottom),
- ObjectAnimator.ofInt(this, "dim", toDim),
- ObjectAnimator.ofFloat(this, "translationY", translationY),
- ObjectAnimator.ofFloat(this, "scaleX", scale),
- ObjectAnimator.ofFloat(this, "scaleY", scale)
- );
- setClipTop(mConfig.systemInsets.top);
- mEnterAnimation.start();
-
- mIsAnimating = true;
- }
-
- /** Animates this view back out of Recents if we were in the process of animating in. */
- public boolean cancelAnimateOnEnterRecents(final Runnable postAnimRunnable) {
- if (mIsAnimating) {
- // Cancel the current animation
- if (mEnterAnimation != null) {
- mEnterAnimation.removeAllListeners();
- mEnterAnimation.cancel();
- }
-
- // Enable the HW Layers on the screenshot view
- mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
-
- // Compose the animation
- mEnterAnimation = new AnimatorSet();
- mEnterAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- post(new Runnable() {
- @Override
- public void run() {
- // Mark that we are no longer animating
- mIsAnimating = false;
- // Disable the HW Layers on the screenshot view
- mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
- // Notify any callbacks
- mCb.onEnterAnimationComplete();
- // Run the given post-anim runnable
- postAnimRunnable.run();
- }
- });
- }
- });
- mEnterAnimation.setDuration(475);
- mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator);
- mEnterAnimation.playTogether(
- ObjectAnimator.ofInt(this, "clipTop", 0),
- ObjectAnimator.ofInt(this, "clipBottom", getMeasuredHeight()),
- ObjectAnimator.ofInt(this, "dim", 0),
- ObjectAnimator.ofFloat(this, "translationY", 0f),
- ObjectAnimator.ofFloat(this, "scaleX", 1f),
- ObjectAnimator.ofFloat(this, "scaleY", 1f)
- );
- mEnterAnimation.start();
-
- return true;
- }
- return false;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index e1c652e..189578c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -24,10 +24,8 @@
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Paint;
import android.graphics.Rect;
import android.net.Uri;
-import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -41,7 +39,6 @@
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
@@ -64,13 +61,10 @@
RecentsConfiguration mConfig;
LayoutInflater mInflater;
- Paint mDebugModePaint;
+ DebugOverlayView mDebugOverlay;
- // The space partitioning root of this container
- SpaceNode mBSP;
- // Search bar view
+ ArrayList<TaskStack> mStacks;
View mSearchBar;
- // Recents view callbacks
RecentsViewCallbacks mCb;
public RecentsView(Context context) {
@@ -96,10 +90,13 @@
mCb = cb;
}
- /** Set/get the bsp root node */
- public void setBSP(SpaceNode n) {
- mBSP = n;
+ /** Sets the debug overlay */
+ public void setDebugOverlay(DebugOverlayView overlay) {
+ mDebugOverlay = overlay;
+ }
+ /** Set/get the bsp root node */
+ public void setTaskStacks(ArrayList<TaskStack> stacks) {
// Remove all TaskStackViews (but leave the search bar)
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
@@ -110,21 +107,18 @@
}
// Create and add all the stacks for this partition of space.
- ArrayList<TaskStack> stacks = mBSP.getStacks();
- for (TaskStack stack : stacks) {
+ mStacks = stacks;
+ int numStacks = mStacks.size();
+ for (int i = 0; i < numStacks; i++) {
+ TaskStack stack = mStacks.get(i);
TaskStackView stackView = new TaskStackView(getContext(), stack);
stackView.setCallbacks(this);
+ // Enable debug mode drawing
+ if (mConfig.debugModeEnabled) {
+ stackView.setDebugOverlay(mDebugOverlay);
+ }
addView(stackView);
}
-
- // Enable debug mode drawing
- if (mConfig.debugModeEnabled) {
- mDebugModePaint = new Paint();
- mDebugModePaint.setColor(0xFFff0000);
- mDebugModePaint.setStyle(Paint.Style.STROKE);
- mDebugModePaint.setStrokeWidth(5f);
- setWillNotDraw(false);
- }
}
/** Launches the focused task from the first stack if possible */
@@ -151,8 +145,8 @@
return false;
}
- /** Launches the first task from the first stack if possible */
- public boolean launchFirstTask() {
+ /** Launches the task that Recents was launched from, if possible */
+ public boolean launchPreviousTask() {
// Get the first stack view
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -162,20 +156,17 @@
TaskStack stack = stackView.mStack;
ArrayList<Task> tasks = stack.getTasks();
- // Get the first task in the stack
+ // Find the launch task in the stack
if (!tasks.isEmpty()) {
- Task task = tasks.get(tasks.size() - 1);
- TaskView tv = null;
-
- // Try and use the first child task view as the source of the launch animation
- if (stackView.getChildCount() > 0) {
- TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1);
- if (stv.getTask() == task) {
- tv = stv;
+ int taskCount = tasks.size();
+ for (int j = 0; j < taskCount; j++) {
+ if (tasks.get(j).isLaunchTarget) {
+ Task task = tasks.get(j);
+ TaskView tv = stackView.getChildViewForTask(task);
+ onTaskViewClicked(stackView, tv, stack, task, false);
+ return true;
}
}
- onTaskViewClicked(stackView, tv, stack, task, false);
- return true;
}
}
}
@@ -243,35 +234,31 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
// Get the search bar bounds and measure the search bar layout
if (mSearchBar != null) {
Rect searchBarSpaceBounds = new Rect();
- mConfig.getSearchBarBounds(width, height - mConfig.systemInsets.top, searchBarSpaceBounds);
+ mConfig.getSearchBarBounds(width, height, mConfig.systemInsets.top, searchBarSpaceBounds);
mSearchBar.measure(
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
}
- // We give the full width of the space, not including the right nav bar insets in landscape,
- // to the stack view, since we want the tasks to render under the search bar in landscape.
- // In addition, we give it the full height, not including the top inset or search bar space,
- // since we want the tasks to render under the navigation buttons in portrait.
Rect taskStackBounds = new Rect();
- mConfig.getTaskStackBounds(width, height, taskStackBounds);
- int childWidth = width - mConfig.systemInsets.right;
- int childHeight = taskStackBounds.height() - mConfig.systemInsets.top;
+ mConfig.getTaskStackBounds(width, height, mConfig.systemInsets.top,
+ mConfig.systemInsets.right, taskStackBounds);
- // Measure each TaskStackView
+ // Measure each TaskStackView with the full width and height of the window since the
+ // transition view is a child of that stack view
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child != mSearchBar && child.getVisibility() != GONE) {
- child.measure(MeasureSpec.makeMeasureSpec(childWidth, widthMode),
- MeasureSpec.makeMeasureSpec(childHeight, heightMode));
+ TaskStackView tsv = (TaskStackView) child;
+ // Set the insets to be the top/left inset + search bounds
+ tsv.setStackInsetRect(taskStackBounds);
+ tsv.measure(widthMeasureSpec, heightMeasureSpec);
}
}
@@ -286,49 +273,30 @@
// Get the search bar bounds so that we lay it out
if (mSearchBar != null) {
Rect searchBarSpaceBounds = new Rect();
- mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds);
- mSearchBar.layout(mConfig.systemInsets.left + searchBarSpaceBounds.left,
- mConfig.systemInsets.top + searchBarSpaceBounds.top,
- mConfig.systemInsets.left + mSearchBar.getMeasuredWidth(),
- mConfig.systemInsets.top + mSearchBar.getMeasuredHeight());
+ mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
+ mConfig.systemInsets.top, searchBarSpaceBounds);
+ mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top,
+ searchBarSpaceBounds.right, searchBarSpaceBounds.bottom);
}
- // We offset the stack view by the left inset (if any), but lay it out under the search bar.
- // In addition, we offset our stack views by the top inset and search bar height, but not
- // the bottom insets because we want it to render under the navigation buttons.
- Rect taskStackBounds = new Rect();
- mConfig.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
- left += mConfig.systemInsets.left;
- top += mConfig.systemInsets.top + taskStackBounds.top;
-
- // Layout each child
- // XXX: Based on the space node for that task view
+ // Layout each TaskStackView with the full width and height of the window since the
+ // transition view is a child of that stack view
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child != mSearchBar && child.getVisibility() != GONE) {
- TaskStackView tsv = (TaskStackView) child;
- child.layout(left, top, left + tsv.getMeasuredWidth(), top + tsv.getMeasuredHeight());
+ child.layout(left, top, left + child.getMeasuredWidth(),
+ top + child.getMeasuredHeight());
}
}
}
@Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- // Debug mode drawing
- if (mConfig.debugModeEnabled) {
- canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mDebugModePaint);
- }
- }
-
- @Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
// Update the configuration with the latest system insets and trigger a relayout
mConfig.updateSystemInsets(insets.getSystemWindowInsets());
requestLayout();
-
- return insets.consumeSystemWindowInsets(false, false, false, true);
+ return insets.consumeSystemWindowInsets();
}
/** Notifies each task view of the user interaction. */
@@ -365,11 +333,12 @@
/** Unfilters any filtered stacks */
public boolean unfilterFilteredStacks() {
- if (mBSP != null) {
+ if (mStacks != null) {
// Check if there are any filtered stacks and unfilter them before we back out of Recents
boolean stacksUnfiltered = false;
- ArrayList<TaskStack> stacks = mBSP.getStacks();
- for (TaskStack stack : stacks) {
+ int numStacks = mStacks.size();
+ for (int i = 0; i < numStacks; i++) {
+ TaskStack stack = mStacks.get(i);
if (stack.hasFilteredTasks()) {
stack.unfilterTasks();
stacksUnfiltered = true;
@@ -452,8 +421,7 @@
public void run() {
if (task.isActive) {
// Bring an active task to the foreground
- RecentsTaskLoader.getInstance().getSystemServicesProxy()
- .moveTaskToFront(task.key.id, launchOpts);
+ ssp.moveTaskToFront(task.key.id, launchOpts);
} else {
// Launch the activity anew with the desired animation
Intent i = new Intent(task.key.baseIntent);
@@ -463,15 +431,9 @@
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
try {
- UserHandle taskUser = new UserHandle(task.userId);
- if (launchOpts != null) {
- getContext().startActivityAsUser(i, launchOpts.toBundle(), taskUser);
-
- } else {
- getContext().startActivityAsUser(i, taskUser);
- if (lockToTask) {
- ssp.lockCurrentTask();
- }
+ ssp.startActivityFromRecents(task.key.id, launchOpts);
+ if (launchOpts == null && lockToTask) {
+ ssp.lockCurrentTask();
}
} catch (ActivityNotFoundException anfe) {
Console.logError(getContext(), "Could not start Activity");
@@ -487,7 +449,7 @@
if (tv == null) {
post(launchRunnable);
} else {
- stackView.animateOnLaunchingTask(tv, launchRunnable);
+ stackView.startLaunchTaskAnimation(tv, launchRunnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index deb9df3..fd636ed 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -22,6 +22,7 @@
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
@@ -43,8 +44,6 @@
RecentsConfiguration mConfig;
- Task mTask;
-
ImageView mDismissButton;
ImageView mApplicationIcon;
TextView mActivityDescription;
@@ -52,7 +51,8 @@
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
- Paint mLayerPaint = new Paint();
+ boolean mIsFullscreen;
+
static Paint sHighlightPaint;
public TaskBarView(Context context) {
@@ -113,11 +113,18 @@
@Override
protected void onDraw(Canvas canvas) {
- // Draw the highlight at the top edge (but put the bottom edge just out of view)
- float offset = mConfig.taskViewHighlightPx / 2f;
- float radius = mConfig.taskViewRoundedCornerRadiusPx;
- canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
- getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
+ if (!mIsFullscreen) {
+ // Draw the highlight at the top edge (but put the bottom edge just out of view)
+ float offset = mConfig.taskViewHighlightPx / 2f;
+ float radius = mConfig.taskViewRoundedCornerRadiusPx;
+ canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
+ getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
+ }
+ }
+
+ /** Sets whether the current task is full screen or not. */
+ void setIsFullscreen(boolean isFullscreen) {
+ mIsFullscreen = isFullscreen;
}
/** Synchronizes this bar view's properties with the task's transform */
@@ -149,7 +156,6 @@
/** Binds the bar view to the task */
void rebindToTask(Task t) {
- mTask = t;
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
// otherwise, we fall back to the application icon
if (t.activityIcon != null) {
@@ -161,7 +167,11 @@
mActivityDescription.setText(t.activityLabel);
}
// Try and apply the system ui tint
- setBackgroundColor(t.colorPrimary);
+ int existingBgColor = (getBackground() instanceof ColorDrawable) ?
+ ((ColorDrawable) getBackground()).getColor() : 0;
+ if (existingBgColor != t.colorPrimary) {
+ setBackgroundColor(t.colorPrimary);
+ }
mActivityDescription.setTextColor(t.useLightOnPrimaryColor ?
mConfig.taskBarViewLightTextColor : mConfig.taskBarViewDarkTextColor);
mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
@@ -170,7 +180,6 @@
/** Unbinds the bar view from the task */
void unbindFromTask() {
- mTask = null;
mApplicationIcon.setImageDrawable(null);
}
@@ -247,14 +256,4 @@
mDismissButton.setAlpha(1f);
}
}
-
- /** Enable the hw layers on this task view */
- void enableHwLayers() {
- mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
- }
-
- /** Disable the hw layers on this task view */
- void disableHwLayers() {
- mDismissButton.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskFooterView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskFooterView.java
new file mode 100644
index 0000000..881bbcf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskFooterView.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+import com.android.systemui.recents.RecentsConfiguration;
+
+
+/** The task footer view */
+public class TaskFooterView extends FrameLayout {
+
+ interface TaskFooterViewCallbacks {
+ public void onTaskFooterHeightChanged(int height, int maxHeight);
+ }
+
+ RecentsConfiguration mConfig;
+
+ TaskFooterViewCallbacks mCb;
+ int mFooterHeight;
+ int mMaxFooterHeight;
+ ObjectAnimator mFooterAnimator;
+
+ public TaskFooterView(Context context) {
+ this(context, null);
+ }
+
+ public TaskFooterView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TaskFooterView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public TaskFooterView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mConfig = RecentsConfiguration.getInstance();
+ mMaxFooterHeight = mConfig.taskViewLockToAppButtonHeight;
+ setFooterHeight(getFooterHeight());
+ }
+
+ /** Sets the callbacks for when the footer height changes. */
+ void setCallbacks(TaskFooterViewCallbacks cb) {
+ mCb = cb;
+ mCb.onTaskFooterHeightChanged(mFooterHeight, mMaxFooterHeight);
+ }
+
+ /** Sets the footer height. */
+ public void setFooterHeight(int footerHeight) {
+ if (footerHeight != mFooterHeight) {
+ mFooterHeight = footerHeight;
+ mCb.onTaskFooterHeightChanged(footerHeight, mMaxFooterHeight);
+ }
+ }
+
+ /** Gets the footer height. */
+ public int getFooterHeight() {
+ return mFooterHeight;
+ }
+
+ /** Animates the footer into and out of view. */
+ void animateFooterVisibility(final boolean visible, int duration) {
+ // Return early if there is no footer
+ if (mMaxFooterHeight <= 0) return;
+
+ // Cancel the previous animation
+ if (mFooterAnimator != null) {
+ mFooterAnimator.removeAllListeners();
+ mFooterAnimator.cancel();
+ }
+ int finalHeight = visible ? mMaxFooterHeight : 0;
+ if (duration > 0) {
+ mFooterAnimator = ObjectAnimator.ofInt(this, "footerHeight", finalHeight);
+ mFooterAnimator.setDuration(duration);
+ mFooterAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
+ mFooterAnimator.start();
+ } else {
+ setFooterHeight(finalHeight);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 8b86258..32b9d8a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -23,7 +23,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Paint;
import android.graphics.Rect;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -73,6 +72,8 @@
ViewPool<TaskView, Task> mViewPool;
ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<TaskViewTransform>();
DozeTrigger mUIDozeTrigger;
+ DebugOverlayView mDebugOverlay;
+ Rect mTaskStackBounds = new Rect();
// The virtual stack scroll that we use for the card layout
int mStackScroll;
@@ -82,7 +83,6 @@
int mFocusedTaskIndex = -1;
OverScroller mScroller;
ObjectAnimator mScrollAnimator;
- boolean mEnableStackClipping = true;
// Optimizations
ReferenceCountedTrigger mHwLayersTrigger;
@@ -92,16 +92,20 @@
boolean mStartEnterAnimationRequestedAfterLayout;
ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext;
int[] mTmpVisibleRange = new int[2];
- Rect mTmpRect = new Rect();
- Rect mTmpRect2 = new Rect();
+ TaskViewTransform mTmpTransform = new TaskViewTransform();
+ HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<Task, TaskView>();
LayoutInflater mInflater;
+ // A convenience runnable to return all views to the pool
Runnable mReturnAllViewsToPoolRunnable = new Runnable() {
@Override
public void run() {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
- mViewPool.returnViewToPool((TaskView) getChildAt(i));
+ TaskView tv = (TaskView) getChildAt(i);
+ mViewPool.returnViewToPool(tv);
+ // Also hide the view since we don't need it anymore
+ tv.setVisibility(View.INVISIBLE);
}
}
};
@@ -128,33 +132,6 @@
}
}
});
- mHwLayersTrigger = new ReferenceCountedTrigger(getContext(), new Runnable() {
- @Override
- public void run() {
- // Enable hw layers on each of the children
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView tv = (TaskView) getChildAt(i);
- tv.enableHwLayers();
- }
- }
- }, new Runnable() {
- @Override
- public void run() {
- // Disable hw layers on each of the children
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView tv = (TaskView) getChildAt(i);
- tv.disableHwLayers();
- }
- }
- }, new Runnable() {
- @Override
- public void run() {
- new Throwable("Invalid hw layers ref count").printStackTrace();
- Console.logError(getContext(), "Invalid HW layers ref count");
- }
- });
}
/** Sets the callbacks */
@@ -162,6 +139,11 @@
mCb = cb;
}
+ /** Sets the debug overlay */
+ public void setDebugOverlay(DebugOverlayView overlay) {
+ mDebugOverlay = overlay;
+ }
+
/** Requests that the views be synchronized with the model */
void requestSynchronizeStackViewsWithModel() {
requestSynchronizeStackViewsWithModel(0);
@@ -179,19 +161,8 @@
}
}
- /** Returns a mapping of child view to Task. */
- HashMap<Task, TaskView> getTaskChildViewMap() {
- HashMap<Task, TaskView> taskViewMap = new HashMap<Task, TaskView>();
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView tv = (TaskView) getChildAt(i);
- taskViewMap.put(tv.getTask(), tv);
- }
- return taskViewMap;
- }
-
/** Finds the child view given a specific task. */
- TaskView getChildViewForTask(Task t) {
+ public TaskView getChildViewForTask(Task t) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
TaskView tv = (TaskView) getChildAt(i);
@@ -210,13 +181,15 @@
/**
* Gets the stack transforms of a list of tasks, and returns the visible range of tasks.
*/
- private void updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
+ private boolean updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
ArrayList<Task> tasks,
int stackScroll,
int[] visibleRangeOut,
boolean boundTranslationsToRect) {
// XXX: We should be intelligent about where to look for the visible stack range using the
// current stack scroll.
+ // XXX: We should log extra cases like the ones below where we don't expect to hit very often
+ // XXX: Print out approximately how many indices we have to go through to find the first visible transform
int taskTransformCount = taskTransforms.size();
int taskCount = tasks.size();
@@ -256,13 +229,14 @@
if (boundTranslationsToRect) {
transform.translationY = Math.min(transform.translationY,
- mStackAlgorithm.mRect.bottom);
+ mStackAlgorithm.mViewRect.bottom);
}
}
if (visibleRangeOut != null) {
visibleRangeOut[0] = frontMostVisibleIndex;
visibleRangeOut[1] = backMostVisibleIndex;
}
+ return frontMostVisibleIndex != -1 && backMostVisibleIndex != -1;
}
/**
@@ -280,65 +254,68 @@
}
/** Synchronizes the views with the model */
- void synchronizeStackViewsWithModel() {
+ boolean synchronizeStackViewsWithModel() {
if (mStackViewsDirty) {
// Get all the task transforms
ArrayList<Task> tasks = mStack.getTasks();
int stackScroll = getStackScroll();
int[] visibleRange = mTmpVisibleRange;
- updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
- TaskViewTransform tmpTransform = new TaskViewTransform();
+ boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
// Return all the invisible children to the pool
- HashMap<Task, TaskView> taskChildViewMap = getTaskChildViewMap();
+ mTmpTaskViewMap.clear();
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
TaskView tv = (TaskView) getChildAt(i);
Task task = tv.getTask();
int taskIndex = mStack.indexOfTask(task);
- if (taskIndex < visibleRange[1] || taskIndex > visibleRange[0]) {
- taskChildViewMap.remove(task);
+ if (visibleRange[1] <= taskIndex && taskIndex <= visibleRange[0]) {
+ mTmpTaskViewMap.put(task, tv);
+ } else {
mViewPool.returnViewToPool(tv);
}
}
// Pick up all the newly visible children and update all the existing children
- boolean isValidVisibleRange = visibleRange[0] != -1 && visibleRange[1] != -1;
for (int i = visibleRange[0]; isValidVisibleRange && i >= visibleRange[1]; i--) {
Task task = tasks.get(i);
TaskViewTransform transform = mCurrentTaskTransforms.get(i);
- TaskView tv = taskChildViewMap.get(task);
+ TaskView tv = mTmpTaskViewMap.get(task);
int taskIndex = mStack.indexOfTask(task);
if (tv == null) {
tv = mViewPool.pickUpViewFromPool(task, task);
+
if (mStackViewsAnimationDuration > 0) {
// For items in the list, put them in start animating them from the
// approriate ends of the list where they are expected to appear
if (transform.t < 0) {
- tmpTransform = mStackAlgorithm.getStackTransform(tasks.get(0), stackScroll, tmpTransform);
+ mTmpTransform = mStackAlgorithm.getStackTransform(tasks.get(0), stackScroll, mTmpTransform);
} else {
- tmpTransform = mStackAlgorithm.getStackTransform(tasks.get(Math.min(tasks.size() - 1, visibleRange[0] + 1)),
- stackScroll, tmpTransform);
+ int nextTaskStackScroll = mStackAlgorithm.getStackScrollForTaskIndex(task, 1);
+ mStackAlgorithm.getStackTransform(nextTaskStackScroll, stackScroll, mTmpTransform);
}
- tv.updateViewPropertiesToTaskTransform(tmpTransform, 0);
+ tv.updateViewPropertiesToTaskTransform(mTmpTransform, 0);
}
}
- // Update and animate the task into place
+ // Animate the task into place
tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
mStackViewsAnimationDuration);
}
+ // Reset the request-synchronize params
mStackViewsAnimationDuration = 0;
mStackViewsDirty = false;
+ return true;
}
+ return false;
}
/** Updates the clip for each of the task views. */
void clipTaskViews() {
// Update the clip on each task child
- if (Constants.DebugFlags.App.EnableTaskStackClipping && mEnableStackClipping) {
+ if (Constants.DebugFlags.App.EnableTaskStackClipping) {
int childCount = getChildCount();
for (int i = 0; i < childCount - 1; i++) {
TaskView tv = (TaskView) getChildAt(i);
@@ -360,34 +337,25 @@
// stacked and we can make assumptions about the visibility of the this
// task relative to the ones in front of it.
if (nextTv != null) {
- // We calculate the bottom clip independent of the footer (since we animate
- // that)
- int scaledMaxFooterHeight = (int) (tv.getMaxFooterHeight() * tv.getScaleX());
- tv.getHitRect(mTmpRect);
- nextTv.getHitRect(mTmpRect2);
- clipBottom = (mTmpRect.bottom - scaledMaxFooterHeight - mTmpRect2.top);
+ // We can reuse the current task transforms to find the task rects
+ TaskViewTransform transform = mCurrentTaskTransforms.get(mStack.indexOfTask(tv.getTask()));
+ TaskViewTransform nextTransform = mCurrentTaskTransforms.get(mStack.indexOfTask(nextTv.getTask()));
+ clipBottom = transform.rect.bottom - nextTransform.rect.top - 200;
}
}
- tv.setClipFromBottom(clipBottom);
+ tv.getViewBounds().setClipBottom(clipBottom);
}
- }
- if (getChildCount() > 0) {
- // The front most task should never be clipped
- TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
- tv.setClipFromBottom(0);
+ if (getChildCount() > 0) {
+ // The front most task should never be clipped
+ TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
+ tv.getViewBounds().setClipBottom(0);
+ }
}
}
- /** Enables/Disables clipping of the tasks in the stack. */
- void setStackClippingEnabled(boolean stackClippingEnabled) {
- if (!stackClippingEnabled) {
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView tv = (TaskView) getChildAt(i);
- tv.setClipFromBottom(0);
- }
- }
- mEnableStackClipping = stackClippingEnabled;
+ /** The stack insets to apply to the stack contents */
+ public void setStackInsetRect(Rect r) {
+ mTaskStackBounds.set(r);
}
/** Sets the current stack scroll */
@@ -396,6 +364,7 @@
mUIDozeTrigger.poke();
requestSynchronizeStackViewsWithModel();
}
+
/** Sets the current stack scroll without synchronizing the stack view with the model */
public void setStackScrollRaw(int value) {
mStackScroll = value;
@@ -408,7 +377,7 @@
/** Computes the initial stack scroll for the stack. */
int getInitialStackScroll() {
if (mStack.getTaskCount() > 2) {
- return mMaxScroll - (int) (mStackAlgorithm.mTaskRect.height() * (3f/4f));
+ return Math.max(mMinScroll, mMaxScroll - (int) (mStackAlgorithm.mTaskRect.height() * (3f/4f)));
}
return mMaxScroll;
}
@@ -423,17 +392,8 @@
int curScroll = getStackScroll();
int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
if (newScroll != curScroll) {
- // Enable hw layers on the stack
- addHwLayersRefCount("animateBoundScroll");
-
// Start a new scroll animation
- animateScroll(curScroll, newScroll, new Runnable() {
- @Override
- public void run() {
- // Disable hw layers on the stack
- decHwLayersRefCount("animateBoundScroll");
- }
- });
+ animateScroll(curScroll, newScroll, null);
}
return mScrollAnimator;
}
@@ -478,8 +438,6 @@
if (!mScroller.isFinished()) {
// Abort the scroller
mScroller.abortAnimation();
- // And disable hw layers on the stack
- decHwLayersRefCount("flingScroll");
}
}
@@ -521,7 +479,7 @@
/** Returns whether the specified scroll is out of bounds */
boolean isScrollOutOfBounds() {
- return getScrollAmountOutOfBounds(getStackScroll()) != 0;
+ return getScrollAmountOutOfBounds(mStackScroll) != 0;
}
/** Updates the min and max virtual scroll bounds */
@@ -537,20 +495,6 @@
}
}
- /** Animates a task view in this stack as it launches. */
- public void animateOnLaunchingTask(TaskView tv, final Runnable r) {
- // Hide each of the task bar dismiss buttons
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView t = (TaskView) getChildAt(i);
- if (t == tv) {
- t.startLaunchTaskAnimation(r, true);
- } else {
- t.startLaunchTaskAnimation(null, false);
- }
- }
- }
-
/** Focuses the task at the specified index in the stack */
void focusTask(int taskIndex, boolean scrollToNewPosition) {
if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
@@ -603,17 +547,6 @@
focusTask(mFocusedTaskIndex, true);
}
- /** Enables the hw layers and increments the hw layer requirement ref count */
- void addHwLayersRefCount(String reason) {
- mHwLayersTrigger.increment();
- }
-
- /** Decrements the hw layer requirement ref count and disables the hw layers when we don't
- need them anymore. */
- void decHwLayersRefCount(String reason) {
- mHwLayersTrigger.decrement();
- }
-
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mTouchHandler.onInterceptTouchEvent(ev);
@@ -629,34 +562,29 @@
if (mScroller.computeScrollOffset()) {
setStackScroll(mScroller.getCurrY());
invalidate();
-
- // If we just finished scrolling, then disable the hw layers
- if (mScroller.isFinished()) {
- decHwLayersRefCount("finishedFlingScroll");
- }
}
}
@Override
public void dispatchDraw(Canvas canvas) {
- synchronizeStackViewsWithModel();
- clipTaskViews();
+ if (synchronizeStackViewsWithModel()) {
+ clipTaskViews();
+ }
super.dispatchDraw(canvas);
}
/** Computes the stack and task rects */
- public void computeRects(int width, int height, int insetLeft, int insetBottom) {
+ public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
// Compute the rects in the stack algorithm
- mStackAlgorithm.computeRects(mStack.getTasks(), width, height, insetLeft, insetBottom);
+ mStackAlgorithm.computeRects(mStack.getTasks(), windowWidth, windowHeight, taskStackBounds);
// Update the scroll bounds
updateMinMaxScroll(false);
}
/**
- * This is called with the size of the space not including the top or right insets, or the
- * search bar height in portrait (but including the search bar width in landscape, since we want
- * to draw under it.
+ * This is called with the full window width and height to allow stack view children to
+ * perform the full screen transition down.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
@@ -664,25 +592,31 @@
int height = MeasureSpec.getSize(heightMeasureSpec);
// Compute our stack/task rects
- Rect taskStackBounds = new Rect();
- mConfig.getTaskStackBounds(width, height, taskStackBounds);
- computeRects(width, height, taskStackBounds.left, mConfig.systemInsets.bottom);
+ Rect taskStackBounds = new Rect(mTaskStackBounds);
+ taskStackBounds.bottom -= mConfig.systemInsets.bottom;
+ computeRects(width, height, taskStackBounds);
// If this is the first layout, then scroll to the front of the stack and synchronize the
- // stack views immediately
+ // stack views immediately to load all the views
if (mAwaitingFirstLayout) {
setStackScrollToInitialState();
requestSynchronizeStackViewsWithModel();
synchronizeStackViewsWithModel();
}
- // Measure each of the children
+ // Measure each of the TaskViews
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
- TaskView t = (TaskView) getChildAt(i);
- t.measure(MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.width(), MeasureSpec.EXACTLY),
+ TaskView tv = (TaskView) getChildAt(i);
+ if (tv.isFullScreenView()) {
+ tv.measure(widthMeasureSpec, heightMeasureSpec);
+ } else {
+ tv.measure(
+ MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.width(),
+ MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.height() +
- t.getMaxFooterHeight(), MeasureSpec.EXACTLY));
+ tv.getMaxFooterHeight(), MeasureSpec.EXACTLY));
+ }
}
setMeasuredDimension(width, height);
@@ -698,54 +632,60 @@
// Layout each of the children
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
- TaskView t = (TaskView) getChildAt(i);
- t.layout(mStackAlgorithm.mTaskRect.left, mStackAlgorithm.mStackRectSansPeek.top,
- mStackAlgorithm.mTaskRect.right, mStackAlgorithm.mStackRectSansPeek.top +
- mStackAlgorithm.mTaskRect.height() + t.getMaxFooterHeight());
+ TaskView tv = (TaskView) getChildAt(i);
+ if (tv.isFullScreenView()) {
+ tv.layout(left, top, left + tv.getMeasuredWidth(), top + tv.getMeasuredHeight());
+ } else {
+ tv.layout(mStackAlgorithm.mTaskRect.left, mStackAlgorithm.mTaskRect.top,
+ mStackAlgorithm.mTaskRect.right, mStackAlgorithm.mTaskRect.bottom +
+ tv.getMaxFooterHeight());
+ }
}
if (mAwaitingFirstLayout) {
- // Mark that we have completely the first layout
mAwaitingFirstLayout = false;
+ onFirstLayout();
+ }
+ }
- // Find the target task with the specified id
- ArrayList<Task> tasks = mStack.getTasks();
- Task targetTask = null;
- int targetTaskId = mConfig.launchedToTaskId;
- if (targetTaskId != -1) {
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task t = tasks.get(i);
- if (t.key.id == targetTaskId) {
- targetTask = t;
- break;
- }
- }
- }
+ /** Handler for the first layout. */
+ void onFirstLayout() {
+ int offscreenY = mStackAlgorithm.mViewRect.bottom -
+ (mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
- // Prepare the first view for its enter animation
- int offsetTopAlign = -mStackAlgorithm.mTaskRect.top;
- int offscreenY = mStackAlgorithm.mRect.bottom -
- (mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mRect.top);
- for (int i = childCount - 1; i >= 0; i--) {
- TaskView tv = (TaskView) getChildAt(i);
- tv.prepareEnterRecentsAnimation(tv.getTask() == targetTask, offsetTopAlign,
- offscreenY);
+ // Find the launch target task
+ Task launchTargetTask = null;
+ int childCount = getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ TaskView tv = (TaskView) getChildAt(i);
+ Task task = tv.getTask();
+ if (task.isLaunchTarget) {
+ launchTargetTask = task;
+ break;
}
+ }
- // If the enter animation started already and we haven't completed a layout yet, do the
- // enter animation now
- if (mStartEnterAnimationRequestedAfterLayout) {
- startEnterRecentsAnimation(mStartEnterAnimationContext);
- mStartEnterAnimationRequestedAfterLayout = false;
- mStartEnterAnimationContext = null;
- }
+ // Prepare the first view for its enter animation
+ for (int i = childCount - 1; i >= 0; i--) {
+ TaskView tv = (TaskView) getChildAt(i);
+ Task task = tv.getTask();
+ boolean occludesLaunchTarget = (launchTargetTask != null) &&
+ launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
+ tv.prepareEnterRecentsAnimation(task.isLaunchTarget, occludesLaunchTarget, offscreenY);
+ }
- // Update the focused task index to be the next item to the top task
- if (mConfig.launchedWithAltTab) {
- // When alt-tabbing, we focus the next previous task
- focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
- }
+ // If the enter animation started already and we haven't completed a layout yet, do the
+ // enter animation now
+ if (mStartEnterAnimationRequestedAfterLayout) {
+ startEnterRecentsAnimation(mStartEnterAnimationContext);
+ mStartEnterAnimationRequestedAfterLayout = false;
+ mStartEnterAnimationContext = null;
+ }
+
+ // Update the focused task index to be the next item to the top task
+ if (mConfig.launchedWithAltTab) {
+ // When alt-tabbing, we focus the next previous task
+ focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
}
}
@@ -759,39 +699,28 @@
}
if (mStack.getTaskCount() > 0) {
- // Find the target task with the specified id
- ArrayList<Task> tasks = mStack.getTasks();
- Task targetTask = null;
- int targetTaskId = mConfig.launchedToTaskId;
- if (targetTaskId != -1) {
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task t = tasks.get(i);
- if (t.key.id == targetTaskId) {
- targetTask = t;
- break;
- }
+ // Find the launch target task
+ Task launchTargetTask = null;
+ int childCount = getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ TaskView tv = (TaskView) getChildAt(i);
+ Task task = tv.getTask();
+ if (task.isLaunchTarget) {
+ launchTargetTask = task;
+ break;
}
}
- // Find the transform for the target task
- if (targetTask != null) {
- ctx.targetTaskTransform = new TaskViewTransform();
- mStackAlgorithm.getStackTransform(targetTask, getStackScroll(), ctx.targetTaskTransform);
- Rect taskStackBounds = new Rect();
- mConfig.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
- ctx.targetTaskTransform.rect.offset(taskStackBounds.left, taskStackBounds.top);
- }
-
// Animate all the task views into view
- int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
TaskView tv = (TaskView) getChildAt(i);
Task task = tv.getTask();
ctx.currentTaskTransform = new TaskViewTransform();
ctx.currentStackViewIndex = i;
ctx.currentStackViewCount = childCount;
- ctx.isCurrentTaskLaunchTarget = (task == targetTask);
+ ctx.currentTaskRect = mStackAlgorithm.mTaskRect;
+ ctx.currentTaskOccludesLaunchTarget = (launchTargetTask != null) &&
+ launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
mStackAlgorithm.getStackTransform(task, getStackScroll(), ctx.currentTaskTransform);
tv.startEnterRecentsAnimation(ctx);
}
@@ -810,8 +739,8 @@
/** Requests this task stacks to start it's exit-recents animation. */
public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
// Animate all the task views into view
- ctx.offscreenTranslationY = mStackAlgorithm.mRect.bottom -
- (mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mRect.top);
+ ctx.offscreenTranslationY = mStackAlgorithm.mViewRect.bottom -
+ (mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
TaskView tv = (TaskView) getChildAt(i);
@@ -822,6 +751,24 @@
ctx.postAnimationTrigger.addLastDecrementRunnable(mReturnAllViewsToPoolRunnable);
}
+ /** Animates a task view in this stack as it launches. */
+ public void startLaunchTaskAnimation(TaskView tv, final Runnable r) {
+ Task launchTargetTask = tv.getTask();
+ int offscreenTranslationY = mStackAlgorithm.mViewRect.bottom -
+ (mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView t = (TaskView) getChildAt(i);
+ if (t == tv) {
+ t.startLaunchTaskAnimation(r, true, true);
+ } else {
+ boolean occludesLaunchTarget = launchTargetTask.group.isTaskAboveTask(t.getTask(),
+ launchTargetTask);
+ t.startLaunchTaskAnimation(null, false, occludesLaunchTarget);
+ }
+ }
+ }
+
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
@@ -967,9 +914,6 @@
// Detach the view from the hierarchy
detachViewFromParent(tv);
- // Disable HW layers
- tv.disableHwLayers();
-
// Reset the view properties
tv.resetViewProperties();
}
@@ -978,6 +922,15 @@
public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) {
// Rebind the task and request that this task's data be filled into the TaskView
tv.onTaskBound(task);
+
+ // Mark the launch task as fullscreen
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition && mAwaitingFirstLayout) {
+ if (task.isLaunchTarget) {
+ tv.setIsFullScreen(true);
+ }
+ }
+
+ // Load the task data
RecentsTaskLoader.getInstance().loadTaskData(task);
// Sanity check, the task view should always be clipping against the stack at this point,
@@ -1013,11 +966,6 @@
} else {
attachViewToParent(tv, insertIndex, tv.getLayoutParams());
}
-
- // Enable hw layers on this view if hw layers are enabled on the stack
- if (mHwLayersTrigger.getCount() > 0) {
- tv.enableHwLayers();
- }
}
@Override
@@ -1067,6 +1015,11 @@
invalidate(mStackAlgorithm.mStackRect);
}
+ @Override
+ public void onTaskViewFullScreenTransitionCompleted() {
+ requestSynchronizeStackViewsWithModel();
+ }
+
/**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 65407a6..0fd4e86 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -17,7 +17,6 @@
package com.android.systemui.recents.views;
import android.graphics.Rect;
-import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
@@ -30,14 +29,14 @@
// These are all going to change
static final float StackOverlapPct = 0.65f; // The overlap height relative to the task height
- static final float StackPeekHeightPct = 0.1f; // The height of the peek space relative to the stack height
+ static final float StackPeekHeightPct = 0.075f; // The height of the peek space relative to the stack height
static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area
static final int StackPeekNumCards = 3; // The number of cards we see in the peek space
RecentsConfiguration mConfig;
// The various rects that define the stack view
- Rect mRect = new Rect();
+ Rect mViewRect = new Rect();
Rect mStackRect = new Rect();
Rect mStackRectSansPeek = new Rect();
Rect mTaskRect = new Rect();
@@ -53,29 +52,21 @@
}
/** Computes the stack and task rects */
- public void computeRects(ArrayList<Task> tasks, int width, int height, int insetLeft, int insetBottom) {
+ public void computeRects(ArrayList<Task> tasks, int windowWidth, int windowHeight,
+ Rect taskStackBounds) {
// Note: We let the stack view be the full height because we want the cards to go under the
// navigation bar if possible. However, the stack rects which we use to calculate
// max scroll, etc. need to take the nav bar into account
// Compute the stack rects
- mRect.set(0, 0, width, height);
- mStackRect.set(mRect);
- mStackRect.left += insetLeft;
- mStackRect.bottom -= insetBottom;
+ mViewRect.set(0, 0, windowWidth, windowHeight);
+ mStackRect.set(taskStackBounds);
int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
int heightPadding = mConfig.taskStackTopPaddingPx;
- if (Constants.DebugFlags.App.EnableSearchLayout) {
- mStackRect.top += heightPadding;
- mStackRect.left += widthPadding;
- mStackRect.right -= widthPadding;
- mStackRect.bottom -= heightPadding;
- } else {
- mStackRect.inset(widthPadding, heightPadding);
- }
+ mStackRect.inset(widthPadding, heightPadding);
mStackRectSansPeek.set(mStackRect);
- mStackRectSansPeek.top += StackPeekHeightPct * mStackRect.height();
+ mStackRectSansPeek.top += StackPeekHeightPct * windowHeight;
// Compute the task rect
int size = mStackRect.width();
@@ -112,12 +103,16 @@
transformOut.reset();
return transformOut;
}
+ return getStackTransform(getStackScrollForTaskIndex(task), stackScroll, transformOut);
+ }
+ /** Update/get the transform */
+ public TaskViewTransform getStackTransform(int taskStackScroll, int stackScroll, TaskViewTransform transformOut) {
// Map the items to an continuous position relative to the specified scroll
int numPeekCards = StackPeekNumCards;
float overlapHeight = StackOverlapPct * mTaskRect.height();
float peekHeight = StackPeekHeightPct * mStackRect.height();
- float t = (getStackScrollForTaskIndex(task) - stackScroll) / overlapHeight;
+ float t = (taskStackScroll - stackScroll) / overlapHeight;
float boundedT = Math.max(t, -(numPeekCards + 1));
// Set the scale relative to its position
@@ -125,8 +120,8 @@
float minScale = StackPeekMinScale;
float scaleRange = 1f - minScale;
float scaleInc = scaleRange / (numPeekCards + numFrontScaledCards);
- float scale = Math.max(minScale, Math.min(1f, minScale +
- ((boundedT + (numPeekCards + 1)) * scaleInc)));
+ float scale = Math.max(minScale, Math.min(1f, minScale +
+ ((boundedT + (numPeekCards + 1)) * scaleInc)));
float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
// Account for the bar offsets being scaled?
float scaleBarYOffset = (1f - scale) * mConfig.taskBarHeight;
@@ -156,7 +151,7 @@
} else {
transformOut.rect.offset(0, transformOut.translationY);
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
- transformOut.visible = Rect.intersects(mRect, transformOut.rect);
+ transformOut.visible = Rect.intersects(mViewRect, transformOut.rect);
}
transformOut.t = t;
return transformOut;
@@ -178,6 +173,14 @@
}
/**
+ * Returns the scroll to such that the task transform at that task + index will have t=0.
+ * (If the scroll is not bounded)
+ */
+ int getStackScrollForTaskIndex(Task t, int relativeIndexOffset) {
+ return mTaskOffsetMap.get(t.key) + (int) (relativeIndexOffset * getTaskOverlapHeight());
+ }
+
+ /**
* Updates the cache of tasks to offsets.
*/
void updateTaskOffsets(ArrayList<Task> tasks) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 191dc37..b1c35f3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -148,8 +148,6 @@
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
- // Enable HW layers
- mSv.addHwLayersRefCount("stackScroll");
}
mLastMotionX = x;
@@ -160,10 +158,6 @@
case MotionEvent.ACTION_UP: {
// Animate the scroll back if we've cancelled
mSv.animateBoundScroll();
- // Disable HW layers
- if (mIsScrolling) {
- mSv.decHwLayersRefCount("stackScroll");
- }
// Reset the drag state and the velocity tracker
mIsScrolling = false;
mActivePointerId = INACTIVE_POINTER_ID;
@@ -241,8 +235,6 @@
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
- // Enable HW layers
- mSv.addHwLayersRefCount("stackScroll");
}
}
if (mIsScrolling) {
@@ -271,9 +263,6 @@
int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
- // Enable HW layers on the stack
- mSv.addHwLayersRefCount("flingScroll");
- // XXX: Make this animation a function of the velocity AND distance
int overscrollRange = (int) (Math.min(1f,
Math.abs((float) velocity / mMaximumVelocity)) *
Constants.Values.TaskStackView.TaskStackOverscrollRange);
@@ -287,14 +276,9 @@
mSv.invalidate(mSv.mStackAlgorithm.mStackRect);
} else if (mSv.isScrollOutOfBounds()) {
// Animate the scroll back into bounds
- // XXX: Make this animation a function of the velocity OR distance
mSv.animateBoundScroll();
}
- if (mIsScrolling) {
- // Disable HW layers
- mSv.decHwLayersRefCount("stackScroll");
- }
mActivePointerId = INACTIVE_POINTER_ID;
mIsScrolling = false;
mTotalScrollMotion = 0;
@@ -315,13 +299,8 @@
break;
}
case MotionEvent.ACTION_CANCEL: {
- if (mIsScrolling) {
- // Disable HW layers
- mSv.decHwLayersRefCount("stackScroll");
- }
if (mSv.isScrollOutOfBounds()) {
// Animate the scroll back into bounds
- // XXX: Make this animation a function of the velocity OR distance
mSv.animateBoundScroll();
}
mActivePointerId = INACTIVE_POINTER_ID;
@@ -351,8 +330,6 @@
TaskView tv = (TaskView) v;
// Disable clipping with the stack while we are swiping
tv.setClipViewInStack(false);
- // Enable HW layers on that task
- tv.enableHwLayers();
// Disallow touch events from this task view
tv.setTouchEnabled(false);
// Hide the footer
@@ -372,10 +349,6 @@
@Override
public void onChildDismissed(View v) {
TaskView tv = (TaskView) v;
- // Disable HW layers on that task
- if (mSv.mHwLayersTrigger.getCount() == 0) {
- tv.disableHwLayers();
- }
// Re-enable clipping with the stack (we will reuse this view)
tv.setClipViewInStack(true);
// Re-enable touch events from this task view
@@ -387,10 +360,6 @@
@Override
public void onSnapBackCompleted(View v) {
TaskView tv = (TaskView) v;
- // Disable HW layers on that task
- if (mSv.mHwLayersTrigger.getCount() == 0) {
- tv.disableHwLayers();
- }
// Re-enable clipping with the stack
tv.setClipViewInStack(true);
// Re-enable touch events from this task view
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
index 08a25f1..1116d51 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.views;
import android.content.Context;
-import android.graphics.Canvas;
+import android.graphics.Bitmap;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
@@ -27,11 +27,8 @@
/** The task thumbnail view */
public class TaskThumbnailView extends FixedSizeImageView {
- Task mTask;
-
// Task bar clipping
- Rect mClipRect;
- boolean mClipTaskBar = true;
+ Rect mClipRect = new Rect();
public TaskThumbnailView(Context context) {
this(context, null);
@@ -50,40 +47,51 @@
setScaleType(ScaleType.FIT_XY);
}
- @Override
- public void draw(Canvas canvas) {
- if (mClipTaskBar && (mClipRect != null)) {
- int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
- canvas.clipRect(mClipRect);
- super.draw(canvas);
- canvas.restoreToCount(restoreCount);
- } else {
- super.draw(canvas);
- }
+ /** Updates the clip rect based on the given task bar. */
+ void enableTaskBarClip(View taskBar) {
+ int top = (int) Math.max(0, taskBar.getTranslationY() +
+ taskBar.getMeasuredHeight() - 1);
+ mClipRect.set(0, top, getMeasuredWidth(), getMeasuredHeight());
+ setClipBounds(mClipRect);
}
- /** Updates the clip rect based on the given task bar. */
- void updateTaskBarClip(View taskBar) {
- // If mClipTaskBar is unset first, then we don't bother setting mTaskBar
- if (mClipTaskBar) {
- int top = (int) Math.max(0, taskBar.getTranslationY() +
- taskBar.getMeasuredHeight() - 1);
- mClipRect = new Rect(0, top, getMeasuredWidth(), getMeasuredHeight());
- invalidate(0, 0, taskBar.getMeasuredWidth(), taskBar.getMeasuredHeight() + 1);
- }
+ /** Convenience method to enable task bar clipping as a runnable. */
+ Runnable enableTaskBarClipAsRunnable(final View taskBar) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ enableTaskBarClip(taskBar);
+ }
+ };
}
/** Disables the task bar clipping. */
- void disableClipTaskBarView() {
- mClipTaskBar = false;
- if (mClipRect != null) {
- invalidate(0, 0, mClipRect.width(), mClipRect.top);
+ Runnable disableTaskBarClipAsRunnable() {
+ return new Runnable() {
+ @Override
+ public void run() {
+ mClipRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ setClipBounds(mClipRect);
+ }
+ };
+ }
+
+ /** Binds the thumbnail view to the screenshot. */
+ boolean bindToScreenshot(Bitmap ss) {
+ if (ss != null) {
+ setImageBitmap(ss);
+ return true;
}
+ return false;
+ }
+
+ /** Unbinds the thumbnail view from the screenshot. */
+ void unbindFromScreenshot() {
+ setImageBitmap(null);
}
/** Binds the thumbnail view to the task */
void rebindToTask(Task t) {
- mTask = t;
if (t.thumbnail != null) {
setImageBitmap(t.thumbnail);
}
@@ -91,7 +99,6 @@
/** Unbinds the thumbnail view from the task */
void unbindFromTask() {
- mTask = null;
setImageDrawable(null);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 4757c5f..e97ce30 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -21,25 +21,25 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Outline;
+import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
-import android.view.ViewOutlineProvider;
-import android.view.ViewPropertyAnimator;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
+import com.android.systemui.recents.AlternateRecentsComponent;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
-
/* A task view */
-public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.OnClickListener,
- View.OnLongClickListener {
+public class TaskView extends FrameLayout implements Task.TaskCallbacks,
+ TaskFooterView.TaskFooterViewCallbacks, View.OnClickListener, View.OnLongClickListener {
/** The TaskView callbacks */
interface TaskViewCallbacks {
public void onTaskViewAppIconClicked(TaskView tv);
@@ -47,29 +47,27 @@
public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask);
public void onTaskViewDismissed(TaskView tv);
public void onTaskViewClipStateChanged(TaskView tv);
+ public void onTaskViewFullScreenTransitionCompleted();
}
RecentsConfiguration mConfig;
- int mFooterHeight;
- int mMaxFooterHeight;
- ObjectAnimator mFooterAnimator;
-
int mDim;
int mMaxDim;
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
+ PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.MULTIPLY);
Task mTask;
boolean mTaskDataLoaded;
boolean mIsFocused;
- boolean mIsStub;
+ boolean mIsFullScreenView;
boolean mClipViewInStack;
- int mClipFromBottom;
+ AnimateableViewBounds mViewBounds;
Paint mLayerPaint = new Paint();
TaskThumbnailView mThumbnailView;
TaskBarView mBarView;
- View mLockToAppButtonView;
+ TaskFooterView mFooterView;
TaskViewCallbacks mCb;
// Optimizations
@@ -80,18 +78,6 @@
updateDimOverlayFromScale();
}
};
- Runnable mEnableThumbnailClip = new Runnable() {
- @Override
- public void run() {
- mThumbnailView.updateTaskBarClip(mBarView);
- }
- };
- Runnable mDisableThumbnailClip = new Runnable() {
- @Override
- public void run() {
- mThumbnailView.disableClipTaskBarView();
- }
- };
public TaskView(Context context) {
@@ -109,55 +95,11 @@
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = RecentsConfiguration.getInstance();
- mMaxFooterHeight = mConfig.taskViewLockToAppButtonHeight;
- setWillNotDraw(false);
- setClipToOutline(true);
- setDim(getDim());
- setFooterHeight(getFooterHeight());
- setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- // The current height is measured with the footer, so account for the footer height
- // and the current clip (in the stack)
- int height = getMeasuredHeight() - mClipFromBottom - mMaxFooterHeight + mFooterHeight;
- outline.setRoundRect(0, 0, getWidth(), height,
- mConfig.taskViewRoundedCornerRadiusPx);
- }
- });
- }
-
- @Override
- protected void onFinishInflate() {
mMaxDim = mConfig.taskStackMaxDim;
-
- // By default, all views are clipped to other views in their stack
mClipViewInStack = true;
-
- // Bind the views
- mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
- mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
- mLockToAppButtonView = findViewById(R.id.lock_to_app);
-
- if (mTaskDataLoaded) {
- onTaskDataLoaded();
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
-
- // Measure the bar view, thumbnail, and lock-to-app buttons
- mBarView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY));
- mLockToAppButtonView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mConfig.taskViewLockToAppButtonHeight,
- MeasureSpec.EXACTLY));
- // Measure the thumbnail height to be the same as the width
- mThumbnailView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY));
- setMeasuredDimension(width, height);
+ mViewBounds = new AnimateableViewBounds(this, mConfig.taskViewRoundedCornerRadiusPx);
+ setOutlineProvider(mViewBounds);
+ setDim(getDim());
}
/** Set callback */
@@ -170,74 +112,72 @@
return mTask;
}
+ /** Returns the view bounds. */
+ AnimateableViewBounds getViewBounds() {
+ return mViewBounds;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ // Bind the views
+ mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
+ mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
+ mFooterView = (TaskFooterView) findViewById(R.id.lock_to_app);
+ if (mConfig.lockToAppEnabled) {
+ mFooterView.setCallbacks(this);
+ } else {
+ mFooterView.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ // Measure the bar view, thumbnail, and footer
+ mBarView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY));
+ mFooterView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(mConfig.taskViewLockToAppButtonHeight,
+ MeasureSpec.EXACTLY));
+ if (mIsFullScreenView) {
+ // Measure the thumbnail height to be the full dimensions
+ mThumbnailView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ } else {
+ // Measure the thumbnail to be square
+ mThumbnailView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY));
+ }
+ setMeasuredDimension(width, height);
+ invalidateOutline();
+ }
+
/** Synchronizes this view's properties with the task's transform */
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
// Update the bar view
mBarView.updateViewPropertiesToTaskTransform(toTransform, duration);
- // Check to see if any properties have changed, and update the task view
- if (duration > 0) {
- ViewPropertyAnimator anim = animate();
- boolean useLayers = false;
-
- // Animate to the final state
- if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
- anim.translationY(toTransform.translationY);
- }
- if (Constants.DebugFlags.App.EnableShadows &&
- toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
- anim.translationZ(toTransform.translationZ);
- }
- if (toTransform.hasScaleChangedFrom(getScaleX())) {
- anim.scaleX(toTransform.scale)
- .scaleY(toTransform.scale)
- .setUpdateListener(mUpdateDimListener);
- useLayers = true;
- }
- if (toTransform.hasAlphaChangedFrom(getAlpha())) {
- // Use layers if we animate alpha
- anim.alpha(toTransform.alpha);
- useLayers = true;
- }
- if (useLayers) {
- anim.withLayer();
- }
- anim.setStartDelay(toTransform.startDelay)
- .setDuration(duration)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .start();
- } else {
- // Set the changed properties
- if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
- setTranslationY(toTransform.translationY);
- }
+ // If we are a full screen view, then only update the Z to keep it in order
+ // XXX: Also update/animate the dim as well
+ if (mIsFullScreenView) {
if (Constants.DebugFlags.App.EnableShadows &&
toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
setTranslationZ(toTransform.translationZ);
}
- if (toTransform.hasScaleChangedFrom(getScaleX())) {
- setScaleX(toTransform.scale);
- setScaleY(toTransform.scale);
- updateDimOverlayFromScale();
- }
- if (toTransform.hasAlphaChangedFrom(getAlpha())) {
- setAlpha(toTransform.alpha);
- }
+ return;
}
+
+ // Apply the transform
+ toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false,
+ mUpdateDimListener);
}
/** Resets this view's properties */
void resetViewProperties() {
- setTranslationX(0f);
- setTranslationY(0f);
- if (Constants.DebugFlags.App.EnableShadows) {
- setTranslationZ(0f);
- }
- setScaleX(1f);
- setScaleY(1f);
- setAlpha(1f);
setDim(0);
- invalidate();
+ TaskViewTransform.reset(this);
}
/**
@@ -262,20 +202,15 @@
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
- public void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, int offsetY,
- int offscreenY) {
+ public void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask,
+ boolean occludesLaunchTarget, int offscreenY) {
if (mConfig.launchedFromAppWithScreenshot) {
if (isTaskViewLaunchTargetTask) {
- // Hide the task view as we are going to animate the full screenshot into view
- // and then replace it with this view once we are done
- setVisibility(View.INVISIBLE);
- // Also hide the front most task bar view so we can animate it in
mBarView.prepareEnterRecentsAnimation();
+ // Hide the footer during the transition in, and animate it out afterwards?
+ mFooterView.animateFooterVisibility(false, 0);
} else {
- // Top align the task views
- setTranslationY(offsetY);
- setScaleX(1f);
- setScaleY(1f);
+ // Don't do anything for the side views when animating in
}
} else if (mConfig.launchedFromAppWithThumbnail) {
@@ -284,6 +219,9 @@
mBarView.prepareEnterRecentsAnimation();
// Set the dim to 0 so we can animate it in
setDim(0);
+ } else if (occludesLaunchTarget) {
+ // Move the task view off screen (below) so we can animate it in
+ setTranslationY(offscreenY);
}
} else if (mConfig.launchedFromHome) {
@@ -299,50 +237,85 @@
/** Animates this task view as it enters recents */
public void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
- TaskViewTransform transform = ctx.currentTaskTransform;
+ final TaskViewTransform transform = ctx.currentTaskTransform;
if (mConfig.launchedFromAppWithScreenshot) {
- if (ctx.isCurrentTaskLaunchTarget) {
- // Animate the full screenshot down first, before swapping with this task view
- ctx.fullScreenshotView.animateOnEnterRecents(ctx, new Runnable() {
+ if (mTask.isLaunchTarget) {
+ Rect taskRect = ctx.currentTaskRect;
+ int duration = mConfig.taskViewEnterFromHomeDuration * 10;
+ int windowInsetTop = mConfig.systemInsets.top; // XXX: Should be for the window
+ float taskScale = ((float) taskRect.width() / getMeasuredWidth()) * transform.scale;
+ float scaledYOffset = ((1f - taskScale) * getMeasuredHeight()) / 2;
+ float scaledWindowInsetTop = (int) (taskScale * windowInsetTop);
+ float scaledTranslationY = taskRect.top + transform.translationY -
+ (scaledWindowInsetTop + scaledYOffset);
+
+ // Animate the top clip
+ mViewBounds.animateClipTop(windowInsetTop, duration,
+ new ValueAnimator.AnimatorUpdateListener() {
@Override
- public void run() {
- // Animate the task bar of the first task view
- mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
- setVisibility(View.VISIBLE);
- // Animate the footer into view
- animateFooterVisibility(true, mConfig.taskBarEnterAnimDuration);
- // Decrement the post animation trigger
- ctx.postAnimationTrigger.decrement();
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int y = (Integer) animation.getAnimatedValue();
+ mBarView.setTranslationY(y);
}
});
- } else {
- // Animate the tasks down behind the full screenshot
+ // Animate the bottom or right clip
+ int size = Math.round((taskRect.width() / taskScale));
+ if (mConfig.hasHorizontalLayout()) {
+ mViewBounds.animateClipRight(getMeasuredWidth() - size, duration);
+ } else {
+ mViewBounds.animateClipBottom(getMeasuredHeight() - (windowInsetTop + size), duration);
+ }
+ // Animate the task bar of the first task view
+ mBarView.startEnterRecentsAnimation(0, null);
animate()
- .scaleX(transform.scale)
- .scaleY(transform.scale)
- .translationY(transform.translationY)
- .setStartDelay(0)
- .setUpdateListener(null)
- .setInterpolator(mConfig.linearOutSlowInInterpolator)
- .setDuration(475)
- .withLayer()
+ .scaleX(taskScale)
+ .scaleY(taskScale)
+ .translationY(scaledTranslationY)
+ .setDuration(duration)
.withEndAction(new Runnable() {
@Override
public void run() {
- mEnableThumbnailClip.run();
+ setIsFullScreen(false);
+ requestLayout();
+
+ // Reset the clip
+ mViewBounds.setClipTop(0);
+ mViewBounds.setClipBottom(0);
+ mViewBounds.setClipRight(0);
+ // Reset the bar translation
+ mBarView.setTranslationY(0);
+ // Enable the thumbnail clip
+ mThumbnailView.enableTaskBarClip(mBarView);
+ // Animate the footer into view (if it is the front most task)
+ animateFooterVisibility(true, mConfig.taskBarEnterAnimDuration);
+
+ // Unbind the thumbnail from the screenshot
+ RecentsTaskLoader.getInstance().loadTaskData(mTask);
+ // Recycle the full screen screenshot
+ AlternateRecentsComponent.consumeLastScreenshot();
+
+ mCb.onTaskViewFullScreenTransitionCompleted();
+
// Decrement the post animation trigger
ctx.postAnimationTrigger.decrement();
}
})
.start();
+ } else {
+ // Otherwise, just enable the thumbnail clip
+ mThumbnailView.enableTaskBarClip(mBarView);
+
+ // Animate the footer into view
+ animateFooterVisibility(true, 0);
}
ctx.postAnimationTrigger.increment();
} else if (mConfig.launchedFromAppWithThumbnail) {
- if (ctx.isCurrentTaskLaunchTarget) {
+ if (mTask.isLaunchTarget) {
// Animate the task bar of the first task view
- mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
+ mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay,
+ mThumbnailView.enableTaskBarClipAsRunnable(mBarView));
// Animate the dim into view as well
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
@@ -360,10 +333,31 @@
ctx.postAnimationTrigger.increment();
// Animate the footer into view
- animateFooterVisibility(true, mConfig.taskBarEnterAnimDuration
- );
+ animateFooterVisibility(true, mConfig.taskBarEnterAnimDuration);
} else {
- mEnableThumbnailClip.run();
+ // Enable the task bar clip
+ mThumbnailView.enableTaskBarClip(mBarView);
+ // Animate the task up if it was occluding the launch target
+ if (ctx.currentTaskOccludesLaunchTarget) {
+ setTranslationY(transform.translationY + mConfig.taskViewAffiliateGroupEnterOffsetPx);
+ setAlpha(0f);
+ animate().alpha(1f)
+ .translationY(transform.translationY)
+ .setStartDelay(mConfig.taskBarEnterAnimDelay)
+ .setUpdateListener(null)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskViewEnterFromHomeDuration)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mThumbnailView.enableTaskBarClip(mBarView);
+ // Decrement the post animation trigger
+ ctx.postAnimationTrigger.decrement();
+ }
+ })
+ .start();
+ ctx.postAnimationTrigger.increment();
+ }
}
} else if (mConfig.launchedFromHome) {
@@ -382,11 +376,10 @@
.setUpdateListener(null)
.setInterpolator(mConfig.quintOutInterpolator)
.setDuration(mConfig.taskViewEnterFromHomeDuration)
- .withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
- mEnableThumbnailClip.run();
+ mThumbnailView.enableTaskBarClip(mBarView);
// Decrement the post animation trigger
ctx.postAnimationTrigger.decrement();
}
@@ -395,11 +388,11 @@
ctx.postAnimationTrigger.increment();
// Animate the footer into view
- animateFooterVisibility(true, mConfig.taskViewEnterFromHomeDuration
- );
+ animateFooterVisibility(true, mConfig.taskViewEnterFromHomeDuration);
+
} else {
// Otherwise, just enable the thumbnail clip
- mEnableThumbnailClip.run();
+ mThumbnailView.enableTaskBarClip(mBarView);
// Animate the footer into view
animateFooterVisibility(true, 0);
@@ -414,17 +407,17 @@
.setUpdateListener(null)
.setInterpolator(mConfig.fastOutLinearInInterpolator)
.setDuration(mConfig.taskViewExitToHomeDuration)
- .withLayer()
.withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
.start();
ctx.postAnimationTrigger.increment();
}
/** Animates this task view as it exits recents */
- public void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask) {
+ public void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask,
+ boolean occludesLaunchTarget) {
if (isLaunchingTask) {
// Disable the thumbnail clip and animate the bar out
- mBarView.startLaunchTaskAnimation(mDisableThumbnailClip, r);
+ mBarView.startLaunchTaskAnimation(mThumbnailView.disableTaskBarClipAsRunnable(), r);
// Animate the dim
if (mDim > 0) {
@@ -436,6 +429,17 @@
} else {
// Hide the dismiss button
mBarView.startLaunchTaskDismissAnimation();
+ // If this is another view in the task grouping and is in front of the launch task,
+ // animate it away first
+ if (occludesLaunchTarget) {
+ animate().alpha(0f)
+ .translationY(getTranslationY() + mConfig.taskViewAffiliateGroupEnterOffsetPx)
+ .setStartDelay(0)
+ .setUpdateListener(null)
+ .setInterpolator(mConfig.fastOutLinearInInterpolator)
+ .setDuration(mConfig.taskBarExitAnimDuration)
+ .start();
+ }
}
}
@@ -450,7 +454,6 @@
.setUpdateListener(null)
.setInterpolator(mConfig.fastOutSlowInInterpolator)
.setDuration(mConfig.taskViewRemoveAnimDuration)
- .withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -478,33 +481,19 @@
mBarView.setNoUserInteractionState();
}
- /** Enable the hw layers on this task view */
- void enableHwLayers() {
- mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
- mBarView.enableHwLayers();
- mLockToAppButtonView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
- }
-
- /** Disable the hw layers on this task view */
- void disableHwLayers() {
- mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
- mBarView.disableHwLayers();
- mLockToAppButtonView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
- }
-
- /** Sets the stubbed state of this task view. */
- void setStubState(boolean isStub) {
- if (!mIsStub && isStub) {
- // This is now a stub task view, so clip to the bar height, hide the thumbnail
- setClipBounds(new Rect(0, 0, getMeasuredWidth(), mBarView.getMeasuredHeight()));
- mThumbnailView.setVisibility(View.INVISIBLE);
- // Temporary
- mBarView.mActivityDescription.setText("Stub");
- } else if (mIsStub && !isStub) {
- setClipBounds(null);
- mThumbnailView.setVisibility(View.VISIBLE);
+ /** Sets whether this task view is full screen or not. */
+ void setIsFullScreen(boolean isFullscreen) {
+ mIsFullScreenView = isFullscreen;
+ mBarView.setIsFullscreen(isFullscreen);
+ if (isFullscreen) {
+ // If we are full screen, then disable the bottom outline clip for the footer
+ mViewBounds.setOutlineClipBottom(0);
}
- mIsStub = isStub;
+ }
+
+ /** Returns whether this task view should currently be drawn as a full screen view. */
+ boolean isFullScreenView() {
+ return mIsFullScreenView;
}
/**
@@ -512,7 +501,7 @@
* view.
*/
boolean shouldClipViewInStack() {
- return mClipViewInStack && (getVisibility() == View.VISIBLE);
+ return mClipViewInStack && !mIsFullScreenView && (getVisibility() == View.VISIBLE);
}
/** Sets whether this view should be clipped, or clipped against. */
@@ -523,81 +512,32 @@
}
}
- void setClipFromBottom(int clipFromBottom) {
- clipFromBottom = Math.max(0, Math.min(getMeasuredHeight(), clipFromBottom));
- if (mClipFromBottom != clipFromBottom) {
- mClipFromBottom = clipFromBottom;
- invalidateOutline();
- }
- }
-
- /** Sets the footer height. */
- public void setFooterHeight(int footerHeight) {
- if (footerHeight != mFooterHeight) {
- mFooterHeight = footerHeight;
- invalidateOutline();
- invalidate(0, getMeasuredHeight() - mMaxFooterHeight, getMeasuredWidth(),
- getMeasuredHeight());
- }
- }
-
- /** Gets the footer height. */
- public int getFooterHeight() {
- return mFooterHeight;
- }
-
/** Gets the max footer height. */
public int getMaxFooterHeight() {
- return mMaxFooterHeight;
+ if (mConfig.lockToAppEnabled) {
+ return mFooterView.mMaxFooterHeight;
+ } else {
+ return 0;
+ }
}
/** Animates the footer into and out of view. */
- public void animateFooterVisibility(boolean visible, int duration) {
- if (!mTask.lockToThisTask) {
- if (mLockToAppButtonView.getVisibility() == View.VISIBLE) {
- mLockToAppButtonView.setVisibility(View.INVISIBLE);
- }
- return;
- }
- if (mMaxFooterHeight <= 0) return;
-
- if (mFooterAnimator != null) {
- mFooterAnimator.removeAllListeners();
- mFooterAnimator.cancel();
- }
- int height = visible ? mMaxFooterHeight : 0;
- if (visible && mLockToAppButtonView.getVisibility() != View.VISIBLE) {
- if (duration > 0) {
- setFooterHeight(0);
- } else {
- setFooterHeight(mMaxFooterHeight);
- }
- mLockToAppButtonView.setVisibility(View.VISIBLE);
- }
- if (duration > 0) {
- mFooterAnimator = ObjectAnimator.ofInt(this, "footerHeight", height);
- mFooterAnimator.setDuration(duration);
- mFooterAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
- if (!visible) {
- mFooterAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mLockToAppButtonView.setVisibility(View.INVISIBLE);
- }
- });
- }
- mFooterAnimator.start();
- } else {
- if (!visible) {
- mLockToAppButtonView.setVisibility(View.INVISIBLE);
- }
- }
+ void animateFooterVisibility(boolean visible, int duration) {
+ // Hide the footer if we are a full screen view
+ if (mIsFullScreenView) return;
+ // Hide the footer if the current task can not be locked to
+ if (!mTask.lockToTaskEnabled || !mTask.lockToThisTask) return;
+ // Otherwise, animate the visibility
+ mFooterView.animateFooterVisibility(visible, duration);
}
/** Returns the current dim. */
public void setDim(int dim) {
mDim = dim;
- postInvalidateOnAnimation();
+ int inverse = 255 - mDim;
+ mDimColorFilter.setColor(Color.argb(0xFF, inverse, inverse, inverse));
+ mLayerPaint.setColorFilter(mDimColorFilter);
+ setLayerType(LAYER_TYPE_HARDWARE, mLayerPaint);
}
/** Returns the current dim. */
@@ -619,24 +559,7 @@
setDim(getDimOverlayFromScale());
}
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
-
- // Apply the dim if necessary
- if (mDim > 0) {
- canvas.drawColor(mDim << 24);
- }
- }
-
- @Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- if (mIsStub && (child == mThumbnailView)) {
- // Skip the thumbnail view if we are in stub mode
- return false;
- }
- return super.drawChild(canvas, child, drawingTime);
- }
+ /**** View focus state ****/
/**
* Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
@@ -691,14 +614,18 @@
public void onTaskDataLoaded() {
if (mThumbnailView != null && mBarView != null) {
// Bind each of the views to the new task data
- mThumbnailView.rebindToTask(mTask);
+ if (mIsFullScreenView) {
+ mThumbnailView.bindToScreenshot(AlternateRecentsComponent.getLastScreenshot());
+ } else {
+ mThumbnailView.rebindToTask(mTask);
+ }
mBarView.rebindToTask(mTask);
// Rebind any listeners
if (Constants.DebugFlags.App.EnableTaskFiltering) {
mBarView.mApplicationIcon.setOnClickListener(this);
}
mBarView.mDismissButton.setOnClickListener(this);
- mLockToAppButtonView.setOnClickListener(this);
+ mFooterView.setOnClickListener(this);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
if (mConfig.developerOptionsEnabled) {
mBarView.mApplicationIcon.setOnLongClickListener(this);
@@ -720,7 +647,7 @@
mBarView.mApplicationIcon.setOnClickListener(null);
}
mBarView.mDismissButton.setOnClickListener(null);
- mLockToAppButtonView.setOnClickListener(null);
+ mFooterView.setOnClickListener(null);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
mBarView.mApplicationIcon.setOnLongClickListener(null);
}
@@ -733,8 +660,23 @@
setOnClickListener(enabled ? this : null);
}
+ /**** TaskFooterView.TaskFooterViewCallbacks ****/
+
@Override
- public void onClick(final View v) {
+ public void onTaskFooterHeightChanged(int height, int maxHeight) {
+ if (mIsFullScreenView) {
+ // Disable the bottom outline clip when fullscreen
+ mViewBounds.setOutlineClipBottom(0);
+ } else {
+ // Update the bottom clip in our outline provider
+ mViewBounds.setOutlineClipBottom(maxHeight - height);
+ }
+ }
+
+ /**** View.OnClickListener Implementation ****/
+
+ @Override
+ public void onClick(final View v) {
// We purposely post the handler delayed to allow for the touch feedback to draw
final TaskView tv = this;
postDelayed(new Runnable() {
@@ -752,13 +694,15 @@
});
// Hide the footer
tv.animateFooterVisibility(false, mConfig.taskViewRemoveAnimDuration);
- } else if (v == tv || v == mLockToAppButtonView) {
- mCb.onTaskViewClicked(tv, tv.getTask(), (v == mLockToAppButtonView));
+ } else if (v == tv || v == mFooterView) {
+ mCb.onTaskViewClicked(tv, tv.getTask(), (v == mFooterView));
}
}
}, 125);
}
+ /**** View.OnLongClickListener Implementation ****/
+
@Override
public boolean onLongClick(View v) {
if (v == mBarView.mApplicationIcon) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index b351b03..aeb4fe4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -16,7 +16,12 @@
package com.android.systemui.recents.views;
+import android.animation.ValueAnimator;
import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+import android.view.animation.Interpolator;
+import com.android.systemui.recents.Constants;
/* The transform state for a task view */
@@ -77,6 +82,72 @@
return (Float.compare(translationZ, v) != 0);
}
+ /** Applies this transform to a view. */
+ public void applyToTaskView(View v, int duration, Interpolator interp, boolean allowLayers,
+ ValueAnimator.AnimatorUpdateListener scaleUpdateListener) {
+ // Check to see if any properties have changed, and update the task view
+ if (duration > 0) {
+ ViewPropertyAnimator anim = v.animate();
+ boolean requiresLayers = false;
+
+ // Animate to the final state
+ if (hasTranslationYChangedFrom(v.getTranslationY())) {
+ anim.translationY(translationY);
+ }
+ if (Constants.DebugFlags.App.EnableShadows &&
+ hasTranslationZChangedFrom(v.getTranslationZ())) {
+ anim.translationZ(translationZ);
+ }
+ if (hasScaleChangedFrom(v.getScaleX())) {
+ anim.scaleX(scale)
+ .scaleY(scale)
+ .setUpdateListener(scaleUpdateListener);
+ requiresLayers = true;
+ }
+ if (hasAlphaChangedFrom(v.getAlpha())) {
+ // Use layers if we animate alpha
+ anim.alpha(alpha);
+ requiresLayers = true;
+ }
+ if (requiresLayers && allowLayers) {
+ anim.withLayer();
+ }
+ anim.setStartDelay(startDelay)
+ .setDuration(duration)
+ .setInterpolator(interp)
+ .start();
+ } else {
+ // Set the changed properties
+ if (hasTranslationYChangedFrom(v.getTranslationY())) {
+ v.setTranslationY(translationY);
+ }
+ if (Constants.DebugFlags.App.EnableShadows &&
+ hasTranslationZChangedFrom(v.getTranslationZ())) {
+ v.setTranslationZ(translationZ);
+ }
+ if (hasScaleChangedFrom(v.getScaleX())) {
+ v.setScaleX(scale);
+ v.setScaleY(scale);
+ scaleUpdateListener.onAnimationUpdate(null);
+ }
+ if (hasAlphaChangedFrom(v.getAlpha())) {
+ v.setAlpha(alpha);
+ }
+ }
+ }
+
+ /** Reset the transform on a view. */
+ public static void reset(View v) {
+ v.setTranslationX(0f);
+ v.setTranslationY(0f);
+ if (Constants.DebugFlags.App.EnableShadows) {
+ v.setTranslationZ(0f);
+ }
+ v.setScaleX(1f);
+ v.setScaleY(1f);
+ v.setAlpha(1f);
+ }
+
@Override
public String toString() {
return "TaskViewTransform delay: " + startDelay + " y: " + translationY + " z: " + translationZ +
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
index e50a5cf..a1fc40f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents.views;
+import android.graphics.Rect;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
/* Common code related to view animations */
@@ -23,27 +24,24 @@
/* The animation context for a task view animation into Recents */
public static class TaskViewEnterContext {
- // The full screenshot view that we are animating down
- FullscreenTransitionOverlayView fullScreenshotView;
- // The transform of the target task view that we are animating into
- TaskViewTransform targetTaskTransform;
// A trigger to run some logic when all the animations complete. This works around the fact
// that it is difficult to coordinate ViewPropertyAnimators
ReferenceCountedTrigger postAnimationTrigger;
// These following properties are updated for each task view we start the enter animation on
+ // Whether or not the current task occludes the launch target
+ boolean currentTaskOccludesLaunchTarget;
+ // The task rect for the current stack
+ Rect currentTaskRect;
// The transform of the current task view
TaskViewTransform currentTaskTransform;
- // Whether this is the front most task view
- boolean isCurrentTaskLaunchTarget;
// The view index of the current task view
int currentStackViewIndex;
// The total number of task views
int currentStackViewCount;
- public TaskViewEnterContext(FullscreenTransitionOverlayView fss, ReferenceCountedTrigger t) {
- fullScreenshotView = fss;
+ public TaskViewEnterContext(ReferenceCountedTrigger t) {
postAnimationTrigger = t;
}
}
@@ -53,6 +51,7 @@
// A trigger to run some logic when all the animations complete. This works around the fact
// that it is difficult to coordinate ViewPropertyAnimators
ReferenceCountedTrigger postAnimationTrigger;
+
// The translationY to apply to a TaskView to move it off the bottom of the task stack
int offscreenTranslationY;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 6d5bb9d..108c8df 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -163,15 +163,17 @@
if (mListening) {
return;
}
+
mBrightnessObserver.startObserving();
mUserTracker.startTracking();
- // Update the slider and mode before attaching the listener so we don't receive the
- // onChanged notifications for the initial values.
+ // Update the slider and mode before attaching the listener so we don't
+ // receive the onChanged notifications for the initial values.
updateMode();
updateSlider();
mControl.setOnChangedListener(this);
+ mListening = true;
}
/** Unregister all call backs, both to and from the controller */
@@ -179,10 +181,11 @@
if (!mListening) {
return;
}
+
mBrightnessObserver.stopObserving();
- mChangeCallbacks.clear();
mUserTracker.stopTracking();
mControl.setOnChangedListener(null);
+ mListening = false;
}
public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index 65e1cc6..ad98168 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -16,6 +16,7 @@
package com.android.systemui.settings;
+import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.res.Resources;
@@ -30,76 +31,66 @@
import com.android.systemui.R;
/** A dialog that provides controls for adjusting the screen brightness. */
-public class BrightnessDialog extends Dialog implements
+public class BrightnessDialog extends Activity implements
BrightnessController.BrightnessStateChangeCallback {
-
- private static final String TAG = "BrightnessDialog";
- private static final boolean DEBUG = false;
-
- protected Handler mHandler = new Handler();
+ private final Handler mHandler = new Handler();
private BrightnessController mBrightnessController;
- private final int mBrightnessDialogLongTimeout;
- private final int mBrightnessDialogShortTimeout;
+ private int mBrightnessDialogLongTimeout;
+ private int mBrightnessDialogShortTimeout;
private final Runnable mDismissDialogRunnable = new Runnable() {
public void run() {
- if (BrightnessDialog.this.isShowing()) {
- BrightnessDialog.this.dismiss();
- }
+ finish();
};
};
-
- public BrightnessDialog(Context ctx) {
- super(ctx);
- Resources r = ctx.getResources();
- mBrightnessDialogLongTimeout =
- r.getInteger(R.integer.quick_settings_brightness_dialog_long_timeout);
- mBrightnessDialogShortTimeout =
- r.getInteger(R.integer.quick_settings_brightness_dialog_short_timeout);
- }
-
-
- /**
- * Create the brightness dialog and any resources that are used for the
- * entire lifetime of the dialog.
- */
@Override
- public void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Window window = getWindow();
- window.setGravity(Gravity.TOP);
- WindowManager.LayoutParams lp = window.getAttributes();
+
+ final Resources r = getResources();
+ mBrightnessDialogLongTimeout = r.getInteger(
+ R.integer.quick_settings_brightness_dialog_long_timeout);
+ mBrightnessDialogShortTimeout = r.getInteger(
+ R.integer.quick_settings_brightness_dialog_short_timeout);
+
+ final Window window = getWindow();
+ final WindowManager.LayoutParams lp = window.getAttributes();
+
// Offset from the top
- lp.y = getContext().getResources().getDimensionPixelOffset(R.dimen.volume_panel_top);
+ lp.y = getResources().getDimensionPixelOffset(R.dimen.volume_panel_top);
lp.type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
- lp.privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+
window.setAttributes(lp);
+ window.setGravity(Gravity.TOP);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.requestFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.quick_settings_brightness_dialog);
- setCanceledOnTouchOutside(true);
}
-
@Override
protected void onStart() {
super.onStart();
- mBrightnessController = new BrightnessController(getContext(),
- (ImageView) findViewById(R.id.brightness_icon),
- (ToggleSlider) findViewById(R.id.brightness_slider));
+
+ final ImageView icon = (ImageView) findViewById(R.id.brightness_icon);
+ final ToggleSlider slider = (ToggleSlider) findViewById(R.id.brightness_slider);
+ mBrightnessController = new BrightnessController(this, icon, slider);
mBrightnessController.registerCallbacks();
- dismissBrightnessDialog(mBrightnessDialogLongTimeout);
mBrightnessController.addStateChangedCallback(this);
+
+ dismissBrightnessDialog(mBrightnessDialogLongTimeout);
}
@Override
protected void onStop() {
super.onStop();
+
+ mBrightnessController.removeStateChangedCallback(this);
mBrightnessController.unregisterCallbacks();
+
removeAllBrightnessDialogCallbacks();
}
@@ -109,6 +100,7 @@
private void dismissBrightnessDialog(int timeout) {
removeAllBrightnessDialogCallbacks();
+
mHandler.postDelayed(mDismissDialogRunnable, timeout);
}
@@ -118,11 +110,12 @@
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
- keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
- keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
- dismiss();
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+ || keyCode == KeyEvent.KEYCODE_VOLUME_UP
+ || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
+ finish();
}
+
return super.onKeyDown(keyCode, event);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/SettingsUI.java b/packages/SystemUI/src/com/android/systemui/settings/SettingsUI.java
deleted file mode 100644
index 8bc72c9..0000000
--- a/packages/SystemUI/src/com/android/systemui/settings/SettingsUI.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2013 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.systemui.settings;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.systemui.SystemUI;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-public class SettingsUI extends SystemUI {
- private static final String TAG = "SettingsUI";
- private static final boolean DEBUG = false;
-
- private final Handler mHandler = new Handler();
- private BrightnessDialog mBrightnessDialog;
-
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)) {
- if (DEBUG) Log.d(TAG, "showing brightness dialog");
-
- if (mBrightnessDialog == null) {
- mBrightnessDialog = new BrightnessDialog(mContext);
- mBrightnessDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- mBrightnessDialog = null;
- }
- });
- }
-
- if (!mBrightnessDialog.isShowing()) {
- mBrightnessDialog.show();
- }
-
- } else {
- Log.w(TAG, "unknown intent: " + intent);
- }
- }
- };
-
- public void start() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
- mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, mHandler);
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.print("mBrightnessDialog=");
- pw.println(mBrightnessDialog == null ? "null" : mBrightnessDialog.toString());
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 0a288d9..04fc02c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -244,9 +244,6 @@
// the user switches to home. We know it is safe to do at this
// point, so make sure new activity switches are now allowed.
ActivityManagerNative.getDefault().resumeAppSwitches();
- // Also, notifications can be launched from the lock screen,
- // so dismiss the lock screen when the activity starts.
- ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
} catch (RemoteException e) {
}
@@ -1012,8 +1009,6 @@
expanded.setExpandedChild(bigContentViewLocal);
}
- PackageManager pm = mContext.getPackageManager();
-
// now the public version
View publicViewLocal = null;
if (publicNotification != null) {
@@ -1034,6 +1029,9 @@
}
if (publicViewLocal == null) {
+ PackageManager pm = getPackageManagerForUser(
+ entry.notification.getUser().getIdentifier());
+
// Add a basic notification template
publicViewLocal = LayoutInflater.from(mContext).inflate(
com.android.internal.R.layout.notification_template_material_base,
@@ -1153,9 +1151,6 @@
// the user switches to home. We know it is safe to do at this
// point, so make sure new activity switches are now allowed.
ActivityManagerNative.getDefault().resumeAppSwitches();
- // Also, notifications can be launched from the lock screen,
- // so dismiss the lock screen when the activity starts.
- ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
} catch (RemoteException e) {
}
@@ -1670,4 +1665,26 @@
// Ignore.
}
}
+
+ /**
+ * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
+ * return PackageManager for mContext
+ */
+ protected PackageManager getPackageManagerForUser(int userId) {
+ Context contextForUser = mContext;
+ // UserHandle defines special userId as negative values, e.g. USER_ALL
+ if (userId >= 0) {
+ try {
+ // Create a context for the correct user so if a package isn't installed
+ // for user 0 we can still load information about the package.
+ contextForUser =
+ mContext.createPackageContextAsUser(mContext.getPackageName(),
+ Context.CONTEXT_RESTRICTED,
+ new UserHandle(userId));
+ } catch (NameNotFoundException e) {
+ // Shouldn't fail to find the package name for system ui.
+ }
+ }
+ return contextForUser.getPackageManager();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
new file mode 100644
index 0000000..845e0ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+import com.android.systemui.R;
+
+/**
+ * An ImageView which does not have overlapping renderings commands and therefore does not need a
+ * layer when alpha is changed.
+ */
+public class KeyguardAffordanceView extends ImageView {
+
+ private static final long CIRCLE_APPEAR_DURATION = 80;
+ private static final long CIRCLE_DISAPPEAR_MAX_DURATION = 200;
+ private static final long NORMAL_ANIMATION_DURATION = 200;
+ public static final float MAX_ICON_SCALE_AMOUNT = 1.5f;
+ public static final float MIN_ICON_SCALE_AMOUNT = 0.8f;
+
+ private final int mMinBackgroundRadius;
+ private final Paint mCirclePaint;
+ private final Interpolator mAppearInterpolator;
+ private final Interpolator mDisappearInterpolator;
+ private final int mInverseColor;
+ private final int mNormalColor;
+ private final ArgbEvaluator mColorInterpolator;
+ private final FlingAnimationUtils mFlingAnimationUtils;
+ private final Drawable mArrowDrawable;
+ private final int mHintChevronPadding;
+ private float mCircleRadius;
+ private int mCenterX;
+ private int mCenterY;
+ private ValueAnimator mCircleAnimator;
+ private ValueAnimator mAlphaAnimator;
+ private ValueAnimator mScaleAnimator;
+ private ValueAnimator mArrowAnimator;
+ private float mCircleStartValue;
+ private boolean mCircleWillBeHidden;
+ private int[] mTempPoint = new int[2];
+ private float mImageScale;
+ private int mCircleColor;
+ private boolean mIsLeft;
+ private float mArrowAlpha = 0.0f;
+ private AnimatorListenerAdapter mCircleEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCircleAnimator = null;
+ }
+ };
+ private AnimatorListenerAdapter mScaleEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mScaleAnimator = null;
+ }
+ };
+ private AnimatorListenerAdapter mAlphaEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAlphaAnimator = null;
+ }
+ };
+ private AnimatorListenerAdapter mArrowEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mArrowAnimator = null;
+ }
+ };
+
+ public KeyguardAffordanceView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardAffordanceView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardAffordanceView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public KeyguardAffordanceView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mCirclePaint = new Paint();
+ mCirclePaint.setAntiAlias(true);
+ mCircleColor = 0xffffffff;
+ mCirclePaint.setColor(mCircleColor);
+
+ mNormalColor = 0xffffffff;
+ mInverseColor = 0xff000000;
+ mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize(
+ R.dimen.keyguard_affordance_min_background_radius);
+ mHintChevronPadding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.hint_chevron_circle_padding);
+ mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
+ android.R.interpolator.linear_out_slow_in);
+ mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
+ android.R.interpolator.fast_out_linear_in);
+ mColorInterpolator = new ArgbEvaluator();
+ mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.3f);
+ mArrowDrawable = context.getDrawable(R.drawable.ic_chevron_left);
+ mArrowDrawable.setBounds(0, 0, mArrowDrawable.getIntrinsicWidth(),
+ mArrowDrawable.getIntrinsicHeight());
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mCenterX = getWidth() / 2;
+ mCenterY = getHeight() / 2;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ drawBackgroundCircle(canvas);
+ drawArrow(canvas);
+ canvas.save();
+ updateIconColor();
+ canvas.scale(mImageScale, mImageScale, getWidth() / 2, getHeight() / 2);
+ super.onDraw(canvas);
+ canvas.restore();
+ }
+
+ private void drawArrow(Canvas canvas) {
+ if (mArrowAlpha > 0) {
+ canvas.save();
+ canvas.translate(mCenterX, mCenterY);
+ if (mIsLeft) {
+ canvas.scale(-1.0f, 1.0f);
+ }
+ canvas.translate(- mCircleRadius - mHintChevronPadding
+ - mArrowDrawable.getIntrinsicWidth() / 2,
+ - mArrowDrawable.getIntrinsicHeight() / 2);
+ mArrowDrawable.setAlpha((int) (mArrowAlpha * 255));
+ mArrowDrawable.draw(canvas);
+ canvas.restore();
+ }
+ }
+
+ private void updateIconColor() {
+ Drawable drawable = getDrawable().mutate();
+ float alpha = mCircleRadius / mMinBackgroundRadius;
+ alpha = Math.min(1.0f, alpha);
+ int color = (int) mColorInterpolator.evaluate(alpha, mNormalColor, mInverseColor);
+ drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ }
+
+ private void drawBackgroundCircle(Canvas canvas) {
+ if (mCircleRadius > 0) {
+ updateCircleColor();
+ canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint);
+ }
+ }
+
+ private void updateCircleColor() {
+ float fraction = 0.5f + 0.5f * Math.max(0.0f, Math.min(1.0f,
+ (mCircleRadius - mMinBackgroundRadius) / (0.5f * mMinBackgroundRadius)));
+ int color = Color.argb((int) (Color.alpha(mCircleColor) * fraction),
+ Color.red(mCircleColor),
+ Color.green(mCircleColor), Color.blue(mCircleColor));
+ mCirclePaint.setColor(color);
+ }
+
+ public void finishAnimation(float velocity, final Runnable mAnimationEndRunnable) {
+ cancelAnimator(mCircleAnimator);
+ float maxCircleSize = getMaxCircleSize();
+ ValueAnimator animatorToRadius = getAnimatorToRadius(maxCircleSize);
+ mFlingAnimationUtils.applyDismissing(animatorToRadius, mCircleRadius, maxCircleSize,
+ velocity, maxCircleSize);
+ animatorToRadius.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimationEndRunnable.run();
+ }
+ });
+ animatorToRadius.start();
+ setImageAlpha(0, true);
+ }
+
+ private float getMaxCircleSize() {
+ getLocationInWindow(mTempPoint);
+ float rootWidth = getRootView().getWidth();
+ float width = mTempPoint[0] + mCenterX;
+ width = Math.max(rootWidth - width, width);
+ float height = mTempPoint[1] + mCenterY;
+ return (float) Math.hypot(width, height);
+ }
+
+ public void setCircleRadius(float circleRadius) {
+ setCircleRadius(circleRadius, false);
+ }
+
+ public void setCircleRadiusWithoutAnimation(float circleRadius) {
+ cancelAnimator(mCircleAnimator);
+ setCircleRadius(circleRadius, true);
+ }
+
+ private void setCircleRadius(float circleRadius, boolean noAnimation) {
+
+ // Check if we need a new animation
+ boolean radiusHidden = (mCircleAnimator != null && mCircleWillBeHidden)
+ || (mCircleAnimator == null && mCircleRadius == 0.0f);
+ boolean nowHidden = circleRadius == 0.0f;
+ boolean radiusNeedsAnimation = (radiusHidden != nowHidden) && !noAnimation;
+ if (!radiusNeedsAnimation) {
+ if (mCircleAnimator == null) {
+ mCircleRadius = circleRadius;
+ invalidate();
+ } else if (!mCircleWillBeHidden) {
+
+ // We just update the end value
+ float diff = circleRadius - mMinBackgroundRadius;
+ PropertyValuesHolder[] values = mCircleAnimator.getValues();
+ values[0].setFloatValues(mCircleStartValue + diff, circleRadius);
+ mCircleAnimator.setCurrentPlayTime(mCircleAnimator.getCurrentPlayTime());
+ }
+ } else {
+ cancelAnimator(mCircleAnimator);
+ ValueAnimator animator = getAnimatorToRadius(circleRadius);
+ Interpolator interpolator = circleRadius == 0.0f
+ ? mDisappearInterpolator
+ : mAppearInterpolator;
+ animator.setInterpolator(interpolator);
+ float durationFactor = Math.abs(mCircleRadius - circleRadius)
+ / (float) mMinBackgroundRadius;
+ long duration = (long) (CIRCLE_APPEAR_DURATION * durationFactor);
+ duration = Math.min(duration, CIRCLE_DISAPPEAR_MAX_DURATION);
+ animator.setDuration(duration);
+ animator.start();
+ }
+ }
+
+ private ValueAnimator getAnimatorToRadius(float circleRadius) {
+ ValueAnimator animator = ValueAnimator.ofFloat(mCircleRadius, circleRadius);
+ mCircleAnimator = animator;
+ mCircleStartValue = mCircleRadius;
+ mCircleWillBeHidden = circleRadius == 0.0f;
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mCircleRadius = (float) animation.getAnimatedValue();
+ invalidate();
+ }
+ });
+ animator.addListener(mCircleEndListener);
+ return animator;
+ }
+
+ private void cancelAnimator(Animator animator) {
+ if (animator != null) {
+ animator.cancel();
+ }
+ }
+
+ public void setImageScale(float imageScale, boolean animate) {
+ setImageScale(imageScale, animate, -1, null);
+ }
+
+ /**
+ * Sets the scale of the containing image
+ *
+ * @param imageScale The new Scale.
+ * @param animate Should an animation be performed
+ * @param duration If animate, whats the duration? When -1 we take the default duration
+ * @param interpolator If animate, whats the interpolator? When null we take the default
+ * interpolator.
+ */
+ public void setImageScale(float imageScale, boolean animate, long duration,
+ Interpolator interpolator) {
+ cancelAnimator(mScaleAnimator);
+ if (!animate) {
+ mImageScale = imageScale;
+ invalidate();
+ } else {
+ ValueAnimator animator = ValueAnimator.ofFloat(mImageScale, imageScale);
+ mScaleAnimator = animator;
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mImageScale = (float) animation.getAnimatedValue();
+ invalidate();
+ }
+ });
+ animator.addListener(mScaleEndListener);
+ if (interpolator == null) {
+ interpolator = imageScale == 0.0f
+ ? mDisappearInterpolator
+ : mAppearInterpolator;
+ }
+ animator.setInterpolator(interpolator);
+ if (duration == -1) {
+ float durationFactor = Math.abs(mImageScale - imageScale)
+ / (1.0f - MIN_ICON_SCALE_AMOUNT);
+ durationFactor = Math.min(1.0f, durationFactor);
+ duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
+ }
+ animator.setDuration(duration);
+ animator.start();
+ }
+ }
+
+ public void setImageAlpha(float alpha, boolean animate) {
+ setImageAlpha(alpha, animate, -1, null, null);
+ }
+
+ /**
+ * Sets the alpha of the containing image
+ *
+ * @param alpha The new alpha.
+ * @param animate Should an animation be performed
+ * @param duration If animate, whats the duration? When -1 we take the default duration
+ * @param interpolator If animate, whats the interpolator? When null we take the default
+ * interpolator.
+ */
+ public void setImageAlpha(float alpha, boolean animate, long duration,
+ Interpolator interpolator, Runnable runnable) {
+ cancelAnimator(mAlphaAnimator);
+ int endAlpha = (int) (alpha * 255);
+ if (!animate) {
+ setImageAlpha(endAlpha);
+ } else {
+ int currentAlpha = getImageAlpha();
+ ValueAnimator animator = ValueAnimator.ofInt(currentAlpha, endAlpha);
+ mAlphaAnimator = animator;
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ setImageAlpha((int) animation.getAnimatedValue());
+ }
+ });
+ animator.addListener(mAlphaEndListener);
+ if (interpolator == null) {
+ interpolator = alpha == 0.0f
+ ? mDisappearInterpolator
+ : mAppearInterpolator;
+ }
+ animator.setInterpolator(interpolator);
+ if (duration == -1) {
+ float durationFactor = Math.abs(currentAlpha - endAlpha) / 255f;
+ durationFactor = Math.min(1.0f, durationFactor);
+ duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
+ }
+ animator.setDuration(duration);
+ if (runnable != null) {
+ animator.addListener(getEndListener(runnable));
+ }
+ animator.start();
+ }
+ }
+
+ private Animator.AnimatorListener getEndListener(final Runnable runnable) {
+ return new AnimatorListenerAdapter() {
+ boolean mCancelled;
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCancelled) {
+ runnable.run();
+ }
+ }
+ };
+ }
+
+ public float getCircleRadius() {
+ return mCircleRadius;
+ }
+
+ public void showArrow(boolean show) {
+ cancelAnimator(mArrowAnimator);
+ float targetAlpha = show ? 1.0f : 0.0f;
+ if (mArrowAlpha == targetAlpha) {
+ return;
+ }
+ ValueAnimator animator = ValueAnimator.ofFloat(mArrowAlpha, targetAlpha);
+ mArrowAnimator = animator;
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mArrowAlpha = (float) animation.getAnimatedValue();
+ invalidate();
+ }
+ });
+ animator.addListener(mArrowEndListener);
+ Interpolator interpolator = show
+ ? mAppearInterpolator
+ : mDisappearInterpolator;
+ animator.setInterpolator(interpolator);
+ float durationFactor = Math.abs(mArrowAlpha - targetAlpha);
+ long duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
+ animator.setDuration(duration);
+ animator.start();
+ }
+
+ public void setIsLeft(boolean left) {
+ mIsLeft = left;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index a41ec22..303454b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -44,6 +44,7 @@
public static final int MODE_TRANSLUCENT = 2;
public static final int MODE_LIGHTS_OUT = 3;
public static final int MODE_TRANSPARENT = 4;
+ public static final int MODE_WARNING = 5;
public static final int LIGHTS_IN_DURATION = 250;
public static final int LIGHTS_OUT_DURATION = 750;
@@ -100,6 +101,7 @@
if (mode == MODE_TRANSLUCENT) return "MODE_TRANSLUCENT";
if (mode == MODE_LIGHTS_OUT) return "MODE_LIGHTS_OUT";
if (mode == MODE_TRANSPARENT) return "MODE_TRANSPARENT";
+ if (mode == MODE_WARNING) return "MODE_WARNING";
throw new IllegalArgumentException("Unknown mode " + mode);
}
@@ -115,6 +117,7 @@
private final int mOpaque;
private final int mSemiTransparent;
private final int mTransparent;
+ private final int mWarning;
private final Drawable mGradient;
private final TimeInterpolator mInterpolator;
@@ -135,10 +138,12 @@
mOpaque = 0xff0000ff;
mSemiTransparent = 0x7f0000ff;
mTransparent = 0x2f0000ff;
+ mWarning = 0xffff0000;
} else {
mOpaque = res.getColor(R.color.system_bar_background_opaque);
mSemiTransparent = res.getColor(R.color.system_bar_background_semi_transparent);
mTransparent = res.getColor(R.color.system_bar_background_transparent);
+ mWarning = res.getColor(R.color.system_bar_background_warning);
}
mGradient = res.getDrawable(gradientResourceId);
mInterpolator = new LinearInterpolator();
@@ -189,7 +194,9 @@
@Override
public void draw(Canvas canvas) {
int targetGradientAlpha = 0, targetColor = 0;
- if (mMode == MODE_TRANSLUCENT) {
+ if (mMode == MODE_WARNING) {
+ targetColor = mWarning;
+ } else if (mMode == MODE_TRANSLUCENT) {
targetColor = mSemiTransparent;
} else if (mMode == MODE_SEMI_TRANSPARENT) {
targetColor = mSemiTransparent;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
new file mode 100644
index 0000000..a8a0cb1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.KeyguardAffordanceView;
+
+/**
+ * A touch handler of the keyguard which is responsible for launching phone and camera affordances.
+ */
+public class KeyguardAffordanceHelper {
+
+ public static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.5f;
+ public static final long HINT_PHASE1_DURATION = 200;
+ private static final long HINT_PHASE2_DURATION = 350;
+ private static final float BACKGROUND_RADIUS_SCALE_FACTOR = 0.15f;
+ private static final int HINT_CIRCLE_OPEN_DURATION = 500;
+
+ private final Context mContext;
+
+ private FlingAnimationUtils mFlingAnimationUtils;
+ private Callback mCallback;
+ private int mTrackingPointer;
+ private VelocityTracker mVelocityTracker;
+ private boolean mSwipingInProgress;
+ private float mInitialTouchX;
+ private float mInitialTouchY;
+ private float mTranslation;
+ private float mTranslationOnDown;
+ private int mTouchSlop;
+ private int mMinTranslationAmount;
+ private int mMinFlingVelocity;
+ private int mHintGrowAmount;
+ private final KeyguardAffordanceView mLeftIcon;
+ private final KeyguardAffordanceView mCenterIcon;
+ private final KeyguardAffordanceView mRightIcon;
+ private Interpolator mAppearInterpolator;
+ private Interpolator mDisappearInterpolator;
+ private Animator mSwipeAnimator;
+ private int mMinBackgroundRadius;
+ private boolean mMotionPerformedByUser;
+ private PowerManager mPM;
+ private AnimatorListenerAdapter mFlingEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mSwipeAnimator = null;
+ setSwipingInProgress(false);
+ }
+ };
+ private Runnable mAnimationEndRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onAnimationToSideEnded();
+ }
+ };
+
+ KeyguardAffordanceHelper(Callback callback, Context context) {
+ mContext = context;
+ mCallback = callback;
+ mLeftIcon = mCallback.getLeftIcon();
+ mLeftIcon.setIsLeft(true);
+ mCenterIcon = mCallback.getCenterIcon();
+ mRightIcon = mCallback.getRightIcon();
+ updateIcon(mLeftIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+ updateIcon(mCenterIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+ updateIcon(mRightIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+ initDimens();
+ mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ }
+
+ private void initDimens() {
+ final ViewConfiguration configuration = ViewConfiguration.get(mContext);
+ mTouchSlop = configuration.getScaledTouchSlop();
+ mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
+ mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
+ R.dimen.keyguard_min_swipe_amount);
+ mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize(
+ R.dimen.keyguard_affordance_min_background_radius);
+ mHintGrowAmount =
+ mContext.getResources().getDimensionPixelSize(R.dimen.hint_grow_amount_sideways);
+ mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
+ mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
+ android.R.interpolator.linear_out_slow_in);
+ mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
+ android.R.interpolator.fast_out_linear_in);
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ int pointerIndex = event.findPointerIndex(mTrackingPointer);
+ if (pointerIndex < 0) {
+ pointerIndex = 0;
+ mTrackingPointer = event.getPointerId(pointerIndex);
+ }
+ final float y = event.getY(pointerIndex);
+ final float x = event.getX(pointerIndex);
+
+ boolean isUp = false;
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ if (mSwipingInProgress) {
+ cancelAnimation();
+ }
+ mInitialTouchY = y;
+ mInitialTouchX = x;
+ mTranslationOnDown = mTranslation;
+ initVelocityTracker();
+ trackMovement(event);
+ mMotionPerformedByUser = false;
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ final int upPointer = event.getPointerId(event.getActionIndex());
+ if (mTrackingPointer == upPointer) {
+ // gesture is ongoing, find a new pointer to track
+ final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+ final float newY = event.getY(newIndex);
+ final float newX = event.getX(newIndex);
+ mTrackingPointer = event.getPointerId(newIndex);
+ mInitialTouchY = newY;
+ mInitialTouchX = newX;
+ mTranslationOnDown = mTranslation;
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ final float w = x - mInitialTouchX;
+ trackMovement(event);
+ if (((leftSwipePossible() && w > mTouchSlop)
+ || (rightSwipePossible() && w < -mTouchSlop))
+ && Math.abs(w) > Math.abs(y - mInitialTouchY)
+ && !mSwipingInProgress) {
+ cancelAnimation();
+ mInitialTouchY = y;
+ mInitialTouchX = x;
+ mTranslationOnDown = mTranslation;
+ setSwipingInProgress(true);
+ }
+ if (mSwipingInProgress) {
+ setTranslation(mTranslationOnDown + x - mInitialTouchX, false, false);
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ isUp = true;
+ case MotionEvent.ACTION_CANCEL:
+ mTrackingPointer = -1;
+ trackMovement(event);
+ if (mSwipingInProgress) {
+ flingWithCurrentVelocity(!isUp);
+ }
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ break;
+ }
+ return true;
+ }
+
+ private void setSwipingInProgress(boolean inProgress) {
+ mSwipingInProgress = inProgress;
+ if (inProgress) {
+ mCallback.onSwipingStarted();
+ }
+ }
+
+ private boolean rightSwipePossible() {
+ return mRightIcon.getVisibility() == View.VISIBLE;
+ }
+
+ private boolean leftSwipePossible() {
+ return mLeftIcon.getVisibility() == View.VISIBLE;
+ }
+
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return false;
+ }
+
+ public void startHintAnimation(boolean right, Runnable onFinishedListener) {
+
+ startHintAnimationPhase1(right, onFinishedListener);
+ }
+
+ private void startHintAnimationPhase1(final boolean right, final Runnable onFinishedListener) {
+ final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
+ targetView.showArrow(true);
+ ValueAnimator animator = getAnimatorToRadius(right, mHintGrowAmount);
+ animator.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCancelled) {
+ mSwipeAnimator = null;
+ onFinishedListener.run();
+ targetView.showArrow(false);
+ } else {
+ startUnlockHintAnimationPhase2(right, onFinishedListener);
+ }
+ }
+ });
+ animator.setInterpolator(mAppearInterpolator);
+ animator.setDuration(HINT_PHASE1_DURATION);
+ animator.start();
+ mSwipeAnimator = animator;
+ }
+
+ /**
+ * Phase 2: Move back.
+ */
+ private void startUnlockHintAnimationPhase2(boolean right, final Runnable onFinishedListener) {
+ final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
+ ValueAnimator animator = getAnimatorToRadius(right, 0);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mSwipeAnimator = null;
+ targetView.showArrow(false);
+ onFinishedListener.run();
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ targetView.showArrow(false);
+ }
+ });
+ animator.setInterpolator(mDisappearInterpolator);
+ animator.setDuration(HINT_PHASE2_DURATION);
+ animator.setStartDelay(HINT_CIRCLE_OPEN_DURATION);
+ animator.start();
+ mSwipeAnimator = animator;
+ }
+
+ private ValueAnimator getAnimatorToRadius(final boolean right, int radius) {
+ final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
+ ValueAnimator animator = ValueAnimator.ofFloat(targetView.getCircleRadius(), radius);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float newRadius = (float) animation.getAnimatedValue();
+ targetView.setCircleRadiusWithoutAnimation(newRadius);
+ float translation = getTranslationFromRadius(newRadius);
+ mTranslation = right ? -translation : translation;
+ updateIconsFromRadius(targetView, newRadius);
+ }
+ });
+ return animator;
+ }
+
+ private void cancelAnimation() {
+ if (mSwipeAnimator != null) {
+ mSwipeAnimator.cancel();
+ }
+ }
+
+ private void flingWithCurrentVelocity(boolean forceSnapBack) {
+ float vel = getCurrentVelocity();
+
+ // We snap back if the current translation is not far enough
+ boolean snapBack = Math.abs(mTranslation) < Math.abs(mTranslationOnDown)
+ + mMinTranslationAmount;
+
+ // or if the velocity is in the opposite direction.
+ boolean velIsInWrongDirection = vel * mTranslation < 0;
+ snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
+ vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
+ fling(vel, snapBack || forceSnapBack);
+ }
+
+ private void fling(float vel, final boolean snapBack) {
+ float target = mTranslation < 0 ? -mCallback.getPageWidth() : mCallback.getPageWidth();
+ target = snapBack ? 0 : target;
+
+ ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
+ mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mTranslation = (float) animation.getAnimatedValue();
+ }
+ });
+ animator.addListener(mFlingEndListener);
+ if (!snapBack) {
+ startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable);
+ mCallback.onAnimationToSideStarted(mTranslation < 0);
+ } else {
+ reset(true);
+ }
+ animator.start();
+ mSwipeAnimator = animator;
+ }
+
+ private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable) {
+ KeyguardAffordanceView targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
+ targetView.finishAnimation(velocity, mAnimationEndRunnable);
+ }
+
+ private void setTranslation(float translation, boolean isReset, boolean animateReset) {
+ translation = rightSwipePossible() ? translation : Math.max(0, translation);
+ translation = leftSwipePossible() ? translation : Math.min(0, translation);
+ float absTranslation = Math.abs(translation);
+ if (absTranslation > Math.abs(mTranslationOnDown) + mMinTranslationAmount ||
+ mMotionPerformedByUser) {
+ userActivity();
+ mMotionPerformedByUser = true;
+ }
+ if (translation != mTranslation || isReset) {
+ KeyguardAffordanceView targetView = translation > 0 ? mLeftIcon : mRightIcon;
+ KeyguardAffordanceView otherView = translation > 0 ? mRightIcon : mLeftIcon;
+ float alpha = absTranslation / mMinTranslationAmount;
+
+ // We interpolate the alpha of the other icons to 0
+ float fadeOutAlpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - alpha);
+ fadeOutAlpha = Math.max(0.0f, fadeOutAlpha);
+
+ // We interpolate the alpha of the targetView to 1
+ alpha = fadeOutAlpha + alpha;
+
+ boolean animateIcons = isReset && animateReset;
+ float radius = getRadiusFromTranslation(absTranslation);
+ if (!isReset) {
+ updateIcon(targetView, radius, alpha, false);
+ } else {
+ updateIcon(targetView, 0.0f, fadeOutAlpha, animateIcons);
+ }
+ updateIcon(otherView, 0.0f, fadeOutAlpha, animateIcons);
+ updateIcon(mCenterIcon, 0.0f, fadeOutAlpha, animateIcons);
+
+ mTranslation = translation;
+ }
+ }
+
+ private void updateIconsFromRadius(KeyguardAffordanceView targetView, float newRadius) {
+ float alpha = newRadius / mMinBackgroundRadius;
+
+ // We interpolate the alpha of the other icons to 0
+ float fadeOutAlpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - alpha);
+ fadeOutAlpha = Math.max(0.0f, fadeOutAlpha);
+
+ // We interpolate the alpha of the targetView to 1
+ alpha = fadeOutAlpha + alpha;
+ KeyguardAffordanceView otherView = targetView == mRightIcon ? mLeftIcon : mRightIcon;
+ updateIconAlpha(targetView, alpha, false);
+ updateIconAlpha(otherView, fadeOutAlpha, false);
+ updateIconAlpha(mCenterIcon, fadeOutAlpha, false);
+ }
+
+ private float getTranslationFromRadius(float circleSize) {
+ float translation = (circleSize - mMinBackgroundRadius) / BACKGROUND_RADIUS_SCALE_FACTOR;
+ return Math.max(0, translation);
+ }
+
+ private float getRadiusFromTranslation(float translation) {
+ return translation * BACKGROUND_RADIUS_SCALE_FACTOR + mMinBackgroundRadius;
+ }
+
+
+ private void userActivity() {
+ mPM.userActivity(SystemClock.uptimeMillis(), false);
+ }
+
+ public void animateHideLeftRightIcon() {
+ updateIcon(mRightIcon, 0f, 0f, true);
+ updateIcon(mLeftIcon, 0f, 0f, true);
+ }
+
+ private void updateIcon(KeyguardAffordanceView view, float circleRadius, float alpha,
+ boolean animate) {
+ if (view.getVisibility() != View.VISIBLE) {
+ return;
+ }
+ view.setCircleRadius(circleRadius);
+ updateIconAlpha(view, alpha, animate);
+ }
+
+ private void updateIconAlpha(KeyguardAffordanceView view, float alpha, boolean animate) {
+ float scale = getScale(alpha);
+ alpha = Math.min(1.0f, alpha);
+ view.setImageAlpha(alpha, animate);
+ view.setImageScale(scale, animate);
+ }
+
+ private float getScale(float alpha) {
+ float scale = alpha / SWIPE_RESTING_ALPHA_AMOUNT * 0.2f +
+ KeyguardAffordanceView.MIN_ICON_SCALE_AMOUNT;
+ return Math.min(scale, KeyguardAffordanceView.MAX_ICON_SCALE_AMOUNT);
+ }
+
+ private void trackMovement(MotionEvent event) {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.addMovement(event);
+ }
+ }
+
+ private void initVelocityTracker() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ }
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+
+ private float getCurrentVelocity() {
+ if (mVelocityTracker == null) {
+ return 0;
+ }
+ mVelocityTracker.computeCurrentVelocity(1000);
+ return mVelocityTracker.getXVelocity();
+ }
+
+ public void onConfigurationChanged() {
+ initDimens();
+ }
+
+ public void reset(boolean animate) {
+ if (mSwipeAnimator != null) {
+ mSwipeAnimator.cancel();
+ }
+ setTranslation(0.0f, true, animate);
+ setSwipingInProgress(false);
+ }
+
+ public interface Callback {
+
+ /**
+ * Notifies the callback when an animation to a side page was started.
+ *
+ * @param rightPage Is the page animated to the right page?
+ */
+ void onAnimationToSideStarted(boolean rightPage);
+
+ /**
+ * Notifies the callback the animation to a side page has ended.
+ */
+ void onAnimationToSideEnded();
+
+ float getPageWidth();
+
+ void onSwipingStarted();
+
+ KeyguardAffordanceView getLeftIcon();
+
+ KeyguardAffordanceView getCenterIcon();
+
+ KeyguardAffordanceView getRightIcon();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index b5f38ae..b9f012c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -38,6 +38,8 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.KeyguardAffordanceView;
/**
* Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
@@ -55,14 +57,15 @@
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
- private ImageView mCameraImageView;
- private ImageView mPhoneImageView;
- private ImageView mLockIcon;
+ private KeyguardAffordanceView mCameraImageView;
+ private KeyguardAffordanceView mPhoneImageView;
+ private KeyguardAffordanceView mLockIcon;
private View mIndicationText;
private ActivityStarter mActivityStarter;
private UnlockMethodCache mUnlockMethodCache;
private LockPatternUtils mLockPatternUtils;
+ private FlashlightController mFlashlightController;
public KeyguardBottomAreaView(Context context) {
super(context);
@@ -85,9 +88,9 @@
protected void onFinishInflate() {
super.onFinishInflate();
mLockPatternUtils = new LockPatternUtils(mContext);
- mCameraImageView = (ImageView) findViewById(R.id.camera_button);
- mPhoneImageView = (ImageView) findViewById(R.id.phone_button);
- mLockIcon = (ImageView) findViewById(R.id.lock_icon);
+ mCameraImageView = (KeyguardAffordanceView) findViewById(R.id.camera_button);
+ mPhoneImageView = (KeyguardAffordanceView) findViewById(R.id.phone_button);
+ mLockIcon = (KeyguardAffordanceView) findViewById(R.id.lock_icon);
mIndicationText = findViewById(R.id.keyguard_indication_text);
watchForCameraPolicyChanges();
watchForAccessibilityChanges();
@@ -96,12 +99,18 @@
mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
mUnlockMethodCache.addListener(this);
updateTrust();
+ setClipChildren(false);
+ setClipToPadding(false);
}
public void setActivityStarter(ActivityStarter activityStarter) {
mActivityStarter = activityStarter;
}
+ public void setFlashlightController(FlashlightController flashlightController) {
+ mFlashlightController = flashlightController;
+ }
+
private Intent getCameraIntent() {
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
boolean currentUserHasTrust = updateMonitor.getUserHasTrust(
@@ -189,6 +198,7 @@
}
public void launchCamera() {
+ mFlashlightController.killFlashlight();
Intent intent = getCameraIntent();
if (intent == SECURE_CAMERA_INTENT) {
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
@@ -221,15 +231,15 @@
mLockIcon.setImageResource(iconRes);
}
- public ImageView getPhoneImageView() {
+ public KeyguardAffordanceView getPhoneView() {
return mPhoneImageView;
}
- public ImageView getCameraImageView() {
+ public KeyguardAffordanceView getCameraView() {
return mCameraImageView;
}
- public ImageView getLockIcon() {
+ public KeyguardAffordanceView getLockIcon() {
return mLockIcon;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
deleted file mode 100644
index d5f9619..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.os.PowerManager;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewPropertyAnimator;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-
-import java.util.ArrayList;
-
-/**
- * A touch handler of the Keyguard which is responsible for swiping the content left or right.
- */
-public class KeyguardPageSwipeHelper {
-
- private static final float SWIPE_MAX_ICON_SCALE_AMOUNT = 2.0f;
- public static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.5f;
- public static final long HINT_PHASE1_DURATION = 250;
- private static final long HINT_PHASE2_DURATION = 450;
-
- private final Context mContext;
-
- private FlingAnimationUtils mFlingAnimationUtils;
- private Callback mCallback;
- private int mTrackingPointer;
- private VelocityTracker mVelocityTracker;
- private boolean mSwipingInProgress;
- private float mInitialTouchX;
- private float mInitialTouchY;
- private float mTranslation;
- private float mTranslationOnDown;
- private int mTouchSlop;
- private int mMinTranslationAmount;
- private int mMinFlingVelocity;
- private int mHintDistance;
- private final View mLeftIcon;
- private final View mCenterIcon;
- private final View mRightIcon;
- private Interpolator mFastOutSlowIn;
- private Interpolator mBounceInterpolator;
- private Animator mSwipeAnimator;
- private boolean mCallbackCalled;
-
- KeyguardPageSwipeHelper(Callback callback, Context context) {
- mContext = context;
- mCallback = callback;
- mLeftIcon = mCallback.getLeftIcon();
- mCenterIcon = mCallback.getCenterIcon();
- mRightIcon = mCallback.getRightIcon();
- updateIcon(mLeftIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
- updateIcon(mCenterIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
- updateIcon(mRightIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
- initDimens();
- }
-
- private void initDimens() {
- final ViewConfiguration configuration = ViewConfiguration.get(mContext);
- mTouchSlop = configuration.getScaledTouchSlop();
- mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
- mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
- R.dimen.keyguard_min_swipe_amount);
- mHintDistance =
- mContext.getResources().getDimensionPixelSize(R.dimen.hint_move_distance_sideways);
- mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
- mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.fast_out_slow_in);
- mBounceInterpolator = new BounceInterpolator();
- }
-
- public boolean onTouchEvent(MotionEvent event) {
- int pointerIndex = event.findPointerIndex(mTrackingPointer);
- if (pointerIndex < 0) {
- pointerIndex = 0;
- mTrackingPointer = event.getPointerId(pointerIndex);
- }
- final float y = event.getY(pointerIndex);
- final float x = event.getX(pointerIndex);
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- if (mSwipingInProgress) {
- cancelAnimations();
- }
- mInitialTouchY = y;
- mInitialTouchX = x;
- mTranslationOnDown = mTranslation;
- initVelocityTracker();
- trackMovement(event);
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- final int upPointer = event.getPointerId(event.getActionIndex());
- if (mTrackingPointer == upPointer) {
- // gesture is ongoing, find a new pointer to track
- final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
- final float newY = event.getY(newIndex);
- final float newX = event.getX(newIndex);
- mTrackingPointer = event.getPointerId(newIndex);
- mInitialTouchY = newY;
- mInitialTouchX = newX;
- mTranslationOnDown = mTranslation;
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- final float w = x - mInitialTouchX;
- trackMovement(event);
- if (((leftSwipePossible() && w > mTouchSlop)
- || (rightSwipePossible() && w < -mTouchSlop))
- && Math.abs(w) > Math.abs(y - mInitialTouchY)
- && !mSwipingInProgress) {
- cancelAnimations();
- mInitialTouchY = y;
- mInitialTouchX = x;
- mTranslationOnDown = mTranslation;
- mSwipingInProgress = true;
- }
- if (mSwipingInProgress) {
- setTranslation(mTranslationOnDown + x - mInitialTouchX, false);
- }
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mTrackingPointer = -1;
- trackMovement(event);
- if (mSwipingInProgress) {
- flingWithCurrentVelocity();
- }
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- break;
- }
- return true;
- }
-
- private boolean rightSwipePossible() {
- return mRightIcon.getVisibility() == View.VISIBLE;
- }
-
- private boolean leftSwipePossible() {
- return mLeftIcon.getVisibility() == View.VISIBLE;
- }
-
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- return false;
- }
-
- public void startHintAnimation(boolean right, Runnable onFinishedListener) {
- startHintAnimationPhase1(right, onFinishedListener);
- }
-
- /**
- * Phase 1: Move everything sidewards.
- */
- private void startHintAnimationPhase1(boolean right, final Runnable onFinishedListener) {
- float target = right ? -mHintDistance : mHintDistance;
- startHintTranslationAnimations(target, HINT_PHASE1_DURATION, mFastOutSlowIn);
- ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mTranslation = (float) animation.getAnimatedValue();
- }
- });
- animator.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCancelled) {
- mSwipeAnimator = null;
- onFinishedListener.run();
- } else {
- startUnlockHintAnimationPhase2(onFinishedListener);
- }
- }
- });
- animator.setInterpolator(mFastOutSlowIn);
- animator.setDuration(HINT_PHASE1_DURATION);
- animator.start();
- mSwipeAnimator = animator;
- }
-
- /**
- * Phase 2: Move back.
- */
- private void startUnlockHintAnimationPhase2(final Runnable onFinishedListener) {
- startHintTranslationAnimations(0f /* target */, HINT_PHASE2_DURATION, mBounceInterpolator);
- ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, 0f);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mTranslation = (float) animation.getAnimatedValue();
- }
- });
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mSwipeAnimator = null;
- onFinishedListener.run();
- }
- });
- animator.setInterpolator(mBounceInterpolator);
- animator.setDuration(HINT_PHASE2_DURATION);
- animator.start();
- mSwipeAnimator = animator;
- }
-
- private void startHintTranslationAnimations(float target, long duration,
- Interpolator interpolator) {
- ArrayList<View> targetViews = mCallback.getTranslationViews();
- for (View targetView : targetViews) {
- targetView.animate()
- .setDuration(duration)
- .setInterpolator(interpolator)
- .translationX(target);
- }
- }
-
- private void cancelAnimations() {
- ArrayList<View> targetViews = mCallback.getTranslationViews();
- for (View target : targetViews) {
- target.animate().cancel();
- }
- View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
- targetView.animate().cancel();
- if (mSwipeAnimator != null) {
- mSwipeAnimator.cancel();
- hideInactiveIcons(true);
- }
- }
-
- private void flingWithCurrentVelocity() {
- float vel = getCurrentVelocity();
-
- // We snap back if the current translation is not far enough
- boolean snapBack = Math.abs(mTranslation) < mMinTranslationAmount;
-
- // or if the velocity is in the opposite direction.
- boolean velIsInWrongDirection = vel * mTranslation < 0;
- snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
- vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
- fling(vel, snapBack);
- }
-
- private void fling(float vel, final boolean snapBack) {
- float target = mTranslation < 0 ? -mCallback.getPageWidth() : mCallback.getPageWidth();
- target = snapBack ? 0 : target;
-
- // translation Animation
- startTranslationAnimations(vel, target);
-
- // animate left / right icon
- startIconAnimation(vel, snapBack, target);
-
- ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
- mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mTranslation = (float) animation.getAnimatedValue();
- }
- });
- animator.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mSwipeAnimator = null;
- mSwipingInProgress = false;
- if (!snapBack && !mCallbackCalled && !mCancelled) {
-
- // ensure that the callback is called eventually
- mCallback.onAnimationToSideStarted(mTranslation < 0);
- mCallbackCalled = true;
- }
- }
- });
- if (!snapBack) {
- mCallbackCalled = false;
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- int frameNumber;
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- if (frameNumber == 2 && !mCallbackCalled) {
-
- // we have to wait for the second frame for this call,
- // until the render thread has definitely kicked in, to avoid a lag.
- mCallback.onAnimationToSideStarted(mTranslation < 0);
- mCallbackCalled = true;
- }
- frameNumber++;
- }
- });
- } else {
- showAllIcons(true);
- }
- animator.start();
- mSwipeAnimator = animator;
- }
-
- private void startTranslationAnimations(float vel, float target) {
- ArrayList<View> targetViews = mCallback.getTranslationViews();
- for (View targetView : targetViews) {
- ViewPropertyAnimator animator = targetView.animate();
- mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
- animator.translationX(target);
- }
- }
-
- private void startIconAnimation(float vel, boolean snapBack, float target) {
- float scale = snapBack ? 1.0f : SWIPE_MAX_ICON_SCALE_AMOUNT;
- float alpha = snapBack ? SWIPE_RESTING_ALPHA_AMOUNT : 1.0f;
- View targetView = mTranslation > 0
- ? mLeftIcon
- : mRightIcon;
- if (targetView.getVisibility() == View.VISIBLE) {
- ViewPropertyAnimator iconAnimator = targetView.animate();
- mFlingAnimationUtils.apply(iconAnimator, mTranslation, target, vel);
- iconAnimator.scaleX(scale);
- iconAnimator.scaleY(scale);
- iconAnimator.alpha(alpha);
- }
- }
-
- private void setTranslation(float translation, boolean isReset) {
- translation = rightSwipePossible() ? translation : Math.max(0, translation);
- translation = leftSwipePossible() ? translation : Math.min(0, translation);
- if (translation != mTranslation || isReset) {
- ArrayList<View> translatedViews = mCallback.getTranslationViews();
- for (View view : translatedViews) {
- view.setTranslationX(translation);
- }
- if (translation == 0.0f) {
- boolean animate = !isReset;
- showAllIcons(animate);
- } else {
- View targetView = translation > 0 ? mLeftIcon : mRightIcon;
- float progress = Math.abs(translation) / mCallback.getPageWidth();
- progress = Math.min(progress, 1.0f);
- float alpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - progress) + progress;
- float scale = (1.0f - progress) + progress * SWIPE_MAX_ICON_SCALE_AMOUNT;
- updateIcon(targetView, scale, alpha, false);
- View otherView = translation < 0 ? mLeftIcon : mRightIcon;
- if (mTranslation * translation <= 0) {
- // The sign of the translation has changed so we need to hide the other icons
- updateIcon(otherView, 0, 0, true);
- updateIcon(mCenterIcon, 0, 0, true);
- }
- }
- mTranslation = translation;
- }
- }
-
- public void showAllIcons(boolean animate) {
- float scale = 1.0f;
- float alpha = SWIPE_RESTING_ALPHA_AMOUNT;
- updateIcon(mRightIcon, scale, alpha, animate);
- updateIcon(mCenterIcon, scale, alpha, animate);
- updateIcon(mLeftIcon, scale, alpha, animate);
- }
-
- public void animateHideLeftRightIcon() {
- updateIcon(mRightIcon, 0f, 0f, true);
- updateIcon(mLeftIcon, 0f, 0f, true);
- }
-
- private void hideInactiveIcons(boolean animate){
- View otherView = mTranslation < 0 ? mLeftIcon : mRightIcon;
- updateIcon(otherView, 0, 0, animate);
- updateIcon(mCenterIcon, 0, 0, animate);
- }
-
- private void updateIcon(View view, float scale, float alpha, boolean animate) {
- if (view.getVisibility() != View.VISIBLE) {
- return;
- }
- if (!animate) {
- view.animate().cancel();
- view.setAlpha(alpha);
- view.setScaleX(scale);
- view.setScaleY(scale);
- // TODO: remove this invalidate once the property setters invalidate it properly
- view.invalidate();
- } else {
- if (view.getAlpha() != alpha || view.getScaleX() != scale) {
- view.animate()
- .setInterpolator(mFastOutSlowIn)
- .alpha(alpha)
- .scaleX(scale)
- .scaleY(scale);
- }
- }
- }
-
- private void trackMovement(MotionEvent event) {
- if (mVelocityTracker != null) {
- mVelocityTracker.addMovement(event);
- }
- }
-
- private void initVelocityTracker() {
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- }
- mVelocityTracker = VelocityTracker.obtain();
- }
-
- private float getCurrentVelocity() {
- if (mVelocityTracker == null) {
- return 0;
- }
- mVelocityTracker.computeCurrentVelocity(1000);
- return mVelocityTracker.getXVelocity();
- }
-
- public void onConfigurationChanged() {
- initDimens();
- }
-
- public void reset() {
- if (mSwipeAnimator != null) {
- mSwipeAnimator.cancel();
- }
- ArrayList<View> targetViews = mCallback.getTranslationViews();
- for (View view : targetViews) {
- view.animate().cancel();
- }
- setTranslation(0.0f, true);
- mSwipingInProgress = false;
- }
-
- public boolean isSwipingInProgress() {
- return mSwipingInProgress;
- }
-
- public interface Callback {
-
- /**
- * Notifies the callback when an animation to a side page was started.
- *
- * @param rightPage Is the page animated to the right page?
- */
- void onAnimationToSideStarted(boolean rightPage);
-
- float getPageWidth();
-
- ArrayList<View> getTranslationViews();
-
- View getLeftIcon();
-
- View getCenterIcon();
-
- View getRightIcon();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 688c0d8..af30266 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -26,7 +26,7 @@
import android.widget.FrameLayout;
import com.android.systemui.qs.QSPanel;
-import com.android.systemui.qs.tiles.UserDetailView;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
/**
* Container for image of the multi user switcher (tappable).
@@ -34,6 +34,8 @@
public class MultiUserSwitch extends FrameLayout implements View.OnClickListener {
private QSPanel mQsPanel;
+ private KeyguardUserSwitcher mKeyguardUserSwitcher;
+ private boolean mKeyguardMode;
public MultiUserSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -49,12 +51,26 @@
mQsPanel = qsPanel;
}
+ public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
+ mKeyguardUserSwitcher = keyguardUserSwitcher;
+ }
+
+ public void setKeyguardMode(boolean keyguardShowing) {
+ mKeyguardMode = keyguardShowing;
+ }
+
@Override
public void onClick(View v) {
final UserManager um = UserManager.get(getContext());
if (um.isUserSwitcherEnabled()) {
- mQsPanel.showDetailAdapter(true,
- mQsPanel.getHost().getUserSwitcherController().userDetailAdapter);
+ if (mKeyguardMode) {
+ if (mKeyguardUserSwitcher != null) {
+ mKeyguardUserSwitcher.show();
+ }
+ } else {
+ mQsPanel.showDetailAdapter(true,
+ mQsPanel.getHost().getUserSwitcherController().userDetailAdapter);
+ }
} else {
Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
getContext(), v, ContactsContract.Profile.CONTENT_URI,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index fc0f2d5..11a38b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.MirrorView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -49,7 +50,7 @@
public class NotificationPanelView extends PanelView implements
ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
- KeyguardPageSwipeHelper.Callback {
+ KeyguardAffordanceHelper.Callback {
// Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
// changed.
@@ -59,7 +60,7 @@
private static final float HEADER_RUBBERBAND_FACTOR = 2.15f;
private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f;
- private KeyguardPageSwipeHelper mPageSwiper;
+ private KeyguardAffordanceHelper mAfforanceHelper;
private StatusBarHeaderView mHeader;
private View mQsContainer;
private QSPanel mQsPanel;
@@ -124,7 +125,6 @@
private boolean mIsExpanding;
private boolean mBlockTouches;
- private ArrayList<View> mSwipeTranslationViews = new ArrayList<>();
private int mNotificationScrimWaitDistance;
private boolean mTwoFingerQsExpand;
private boolean mTwoFingerQsExpandPossible;
@@ -135,6 +135,10 @@
*/
private int mScrollYOverride = -1;
private boolean mQsAnimatorExpand;
+ private boolean mIsLaunchTransitionFinished;
+ private boolean mIsLaunchTransitionRunning;
+ private Runnable mLaunchAnimationEndRunnable;
+ private boolean mOnlyAffordanceInThisMotion;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -167,9 +171,7 @@
mFastOutLinearInterpolator = AnimationUtils.loadInterpolator(getContext(),
android.R.interpolator.fast_out_linear_in);
mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
- mSwipeTranslationViews.add(mNotificationStackScroller);
- mSwipeTranslationViews.add(mKeyguardStatusView);
- mPageSwiper = new KeyguardPageSwipeHelper(this, getContext());
+ mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
}
@Override
@@ -297,9 +299,10 @@
@Override
public void resetViews() {
+ mIsLaunchTransitionFinished = false;
mBlockTouches = false;
mUnlockIconActive = false;
- mPageSwiper.reset();
+ mAfforanceHelper.reset(true);
closeQs();
mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */,
true /* cancelAnimators */);
@@ -354,6 +357,7 @@
if (mBlockTouches) {
return false;
}
+ resetDownStates(event);
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
@@ -430,6 +434,12 @@
return !mQsExpanded && super.onInterceptTouchEvent(event);
}
+ private void resetDownStates(MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mOnlyAffordanceInThisMotion = false;
+ }
+ }
+
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
@@ -464,15 +474,14 @@
if (mBlockTouches) {
return false;
}
- // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
- // implementation.
+ resetDownStates(event);
if ((!mIsExpanding || mHintAnimationRunning)
&& !mQsExpanded
&& mStatusBar.getBarState() != StatusBarState.SHADE) {
- mPageSwiper.onTouchEvent(event);
- if (mPageSwiper.isSwipingInProgress()) {
- return true;
- }
+ mAfforanceHelper.onTouchEvent(event);
+ }
+ if (mOnlyAffordanceInThisMotion) {
+ return true;
}
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
&& mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
@@ -951,20 +960,16 @@
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
boolean active = getMaxPanelHeight() - getExpandedHeight() > mUnlockMoveDistance;
+ KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
if (active && !mUnlockIconActive && mTracking) {
- mKeyguardBottomArea.getLockIcon().animate()
- .alpha(1f)
- .scaleY(LOCK_ICON_ACTIVE_SCALE)
- .scaleX(LOCK_ICON_ACTIVE_SCALE)
- .setInterpolator(mFastOutLinearInterpolator)
- .setDuration(150);
+ lockIcon.setImageAlpha(1.0f, true, 150, mFastOutLinearInterpolator, null);
+ lockIcon.setImageScale(LOCK_ICON_ACTIVE_SCALE, true, 150,
+ mFastOutLinearInterpolator);
} else if (!active && mUnlockIconActive && mTracking) {
- mKeyguardBottomArea.getLockIcon().animate()
- .alpha(KeyguardPageSwipeHelper.SWIPE_RESTING_ALPHA_AMOUNT)
- .scaleY(1f)
- .scaleX(1f)
- .setInterpolator(mFastOutLinearInterpolator)
- .setDuration(150);
+ lockIcon.setImageAlpha(KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT, true,
+ 150, mFastOutLinearInterpolator, null);
+ lockIcon.setImageScale(1.0f, true, 150,
+ mFastOutLinearInterpolator);
}
mUnlockIconActive = active;
}
@@ -1093,7 +1098,7 @@
super.onTrackingStarted();
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
- mPageSwiper.animateHideLeftRightIcon();
+ mAfforanceHelper.animateHideLeftRightIcon();
}
}
@@ -1106,16 +1111,15 @@
}
if (expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
- mPageSwiper.showAllIcons(true);
+ if (!mHintAnimationRunning) {
+ mAfforanceHelper.reset(true);
+ }
}
if (!expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
- mKeyguardBottomArea.getLockIcon().animate()
- .alpha(0f)
- .scaleX(2f)
- .scaleY(2f)
- .setInterpolator(mFastOutLinearInterpolator)
- .setDuration(100);
+ KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
+ lockIcon.setImageAlpha(0.0f, true, 100, mFastOutLinearInterpolator, null);
+ lockIcon.setImageScale(2.0f, true, 100, mFastOutLinearInterpolator);
}
}
@@ -1141,7 +1145,7 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- mPageSwiper.onConfigurationChanged();
+ mAfforanceHelper.onConfigurationChanged();
}
@Override
@@ -1159,6 +1163,8 @@
@Override
public void onAnimationToSideStarted(boolean rightPage) {
boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? rightPage : !rightPage;
+ mIsLaunchTransitionRunning = true;
+ mLaunchAnimationEndRunnable = null;
if (start) {
mKeyguardBottomArea.launchPhone();
} else {
@@ -1168,20 +1174,29 @@
}
@Override
+ public void onAnimationToSideEnded() {
+ mIsLaunchTransitionRunning = false;
+ mIsLaunchTransitionFinished = true;
+ if (mLaunchAnimationEndRunnable != null) {
+ mLaunchAnimationEndRunnable.run();
+ mLaunchAnimationEndRunnable = null;
+ }
+ }
+
+ @Override
protected void onEdgeClicked(boolean right) {
if ((right && getRightIcon().getVisibility() != View.VISIBLE)
|| (!right && getLeftIcon().getVisibility() != View.VISIBLE)) {
return;
}
mHintAnimationRunning = true;
- mPageSwiper.startHintAnimation(right, new Runnable() {
+ mAfforanceHelper.startHintAnimation(right, new Runnable() {
@Override
public void run() {
mHintAnimationRunning = false;
mStatusBar.onHintFinished();
}
});
- startHighlightIconAnimation(right ? getRightIcon() : getLeftIcon());
boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? right : !right;
if (start) {
mStatusBar.onPhoneHintStarted();
@@ -1199,17 +1214,14 @@
/**
* Starts the highlight (making it fully opaque) animation on an icon.
*/
- private void startHighlightIconAnimation(final View icon) {
- icon.animate()
- .alpha(1.0f)
- .setDuration(KeyguardPageSwipeHelper.HINT_PHASE1_DURATION)
- .setInterpolator(mFastOutSlowInInterpolator)
- .withEndAction(new Runnable() {
+ private void startHighlightIconAnimation(final KeyguardAffordanceView icon) {
+ icon.setImageAlpha(1.0f, true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
+ mFastOutSlowInInterpolator, new Runnable() {
@Override
public void run() {
- icon.animate().alpha(KeyguardPageSwipeHelper.SWIPE_RESTING_ALPHA_AMOUNT)
- .setDuration(KeyguardPageSwipeHelper.HINT_PHASE1_DURATION)
- .setInterpolator(mFastOutSlowInInterpolator);
+ icon.setImageAlpha(KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT,
+ true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
+ mFastOutSlowInInterpolator, null);
}
});
}
@@ -1220,27 +1232,28 @@
}
@Override
- public ArrayList<View> getTranslationViews() {
- return mSwipeTranslationViews;
+ public void onSwipingStarted() {
+ requestDisallowInterceptTouchEvent(true);
+ mOnlyAffordanceInThisMotion = true;
}
@Override
- public View getLeftIcon() {
+ public KeyguardAffordanceView getLeftIcon() {
return getLayoutDirection() == LAYOUT_DIRECTION_RTL
- ? mKeyguardBottomArea.getCameraImageView()
- : mKeyguardBottomArea.getPhoneImageView();
+ ? mKeyguardBottomArea.getCameraView()
+ : mKeyguardBottomArea.getPhoneView();
}
@Override
- public View getCenterIcon() {
+ public KeyguardAffordanceView getCenterIcon() {
return mKeyguardBottomArea.getLockIcon();
}
@Override
- public View getRightIcon() {
+ public KeyguardAffordanceView getRightIcon() {
return getLayoutDirection() == LAYOUT_DIRECTION_RTL
- ? mKeyguardBottomArea.getPhoneImageView()
- : mKeyguardBottomArea.getCameraImageView();
+ ? mKeyguardBottomArea.getPhoneView()
+ : mKeyguardBottomArea.getCameraView();
}
@Override
@@ -1282,4 +1295,16 @@
public boolean shouldDelayChildPressedState() {
return true;
}
+
+ public boolean isLaunchTransitionFinished() {
+ return mIsLaunchTransitionFinished;
+ }
+
+ public boolean isLaunchTransitionRunning() {
+ return mIsLaunchTransitionRunning;
+ }
+
+ public void setLaunchTransitionEndRunnable(Runnable r) {
+ mLaunchAnimationEndRunnable = r;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index fb0f1c1..b3b70e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -28,6 +28,7 @@
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -52,7 +53,6 @@
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.InputMethodService;
@@ -62,6 +62,7 @@
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -69,9 +70,9 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
@@ -127,11 +128,14 @@
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
import com.android.systemui.statusbar.policy.CastControllerImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
+import com.android.systemui.statusbar.policy.HotspotControllerImpl;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.LocationControllerImpl;
import com.android.systemui.statusbar.policy.NetworkControllerImpl;
@@ -198,6 +202,9 @@
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.build();
+ public static final int FADE_KEYGUARD_START_DELAY = 100;
+ public static final int FADE_KEYGUARD_DURATION = 300;
+
PhoneStatusBarPolicy mIconPolicy;
// These are no longer handled by the policy, because we need custom strategies for them
@@ -205,6 +212,7 @@
BatteryController mBatteryController;
LocationControllerImpl mLocationController;
NetworkControllerImpl mNetworkController;
+ HotspotControllerImpl mHotspotController;
RotationLockControllerImpl mRotationLockController;
UserInfoController mUserInfoController;
ZenModeController mZenModeController;
@@ -213,6 +221,7 @@
KeyguardUserSwitcher mKeyguardUserSwitcher;
FlashlightController mFlashlightController;
UserSwitcherController mUserSwitcherController;
+ KeyguardMonitor mKeyguardMonitor;
int mNaturalBarHeight = -1;
int mIconSize = -1;
@@ -436,6 +445,8 @@
private final ShadeUpdates mShadeUpdates = new ShadeUpdates();
private int mDrawCount;
+ private Runnable mLaunchTransitionEndRunnable;
+ private boolean mLaunchTransitionFadingAway;
private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD
| ViewState.LOCATION_TOP_STACK_PEEKING
@@ -703,7 +714,18 @@
// Other icons
mLocationController = new LocationControllerImpl(mContext); // will post a notification
mBatteryController = new BatteryController(mContext);
+ mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
+ @Override
+ public void onPowerSaveChanged() {
+ mHandler.post(mCheckBarModes);
+ }
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ // noop
+ }
+ });
mNetworkController = new NetworkControllerImpl(mContext);
+ mHotspotController = new HotspotControllerImpl(mContext);
mBluetoothController = new BluetoothControllerImpl(mContext);
if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
mRotationLockController = new RotationLockControllerImpl(mContext);
@@ -715,9 +737,6 @@
final SignalClusterView signalCluster =
(SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
- mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
- (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mHeader);
-
mNetworkController.addSignalCluster(signalCluster);
signalCluster.setNetworkController(mNetworkController);
final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
@@ -749,16 +768,23 @@
}
mFlashlightController = new FlashlightController(mContext);
+ mKeyguardBottomArea.setFlashlightController(mFlashlightController);
mUserSwitcherController = new UserSwitcherController(mContext);
+ mKeyguardMonitor = new KeyguardMonitor();
+
+ mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
+ (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mHeader,
+ mUserSwitcherController);
+
// Set up the quick settings tile panel
mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
if (mQSPanel != null) {
final QSTileHost qsh = new QSTileHost(mContext, this,
mBluetoothController, mLocationController, mRotationLockController,
- mNetworkController, mZenModeController, null /*tethering*/,
- mCastController, mVolumeComponent, mFlashlightController,
- mUserSwitcherController);
+ mNetworkController, mZenModeController, mHotspotController,
+ mCastController, mFlashlightController,
+ mUserSwitcherController, mKeyguardMonitor);
mQSPanel.setHost(qsh);
for (QSTile<?> tile : qsh.getTiles()) {
mQSPanel.addTile(tile);
@@ -1598,24 +1624,8 @@
updateMediaMetaData(metaDataChanged);
}
- private void removeAndRecycleImageViewDrawable(ImageView iv) {
- Bitmap oldBitmap = null;
- final Drawable drawable = iv.getDrawable();
- if (drawable != null && drawable instanceof BitmapDrawable) {
- oldBitmap = ((BitmapDrawable) drawable).getBitmap();
- }
- iv.animate().cancel();
- iv.setImageDrawable(null);
- if (oldBitmap != null) {
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: recycling bitmap " + oldBitmap + " from ImageView " + iv);
- }
- oldBitmap.recycle();
- }
- }
-
/**
- * Hide the album artwork that is fading out and release its memory.
+ * Hide the album artwork that is fading out and release its bitmap.
*/
private Runnable mHideBackdropFront = new Runnable() {
@Override
@@ -1624,7 +1634,8 @@
Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
}
mBackdropFront.setVisibility(View.INVISIBLE);
- removeAndRecycleImageViewDrawable(mBackdropFront);
+ mBackdropFront.animate().cancel();
+ mBackdropFront.setImageDrawable(null);
}
};
@@ -1725,7 +1736,8 @@
}
private int adjustDisableFlags(int state) {
- if (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit) {
+ if (!mLaunchTransitionFadingAway
+ && (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit)) {
state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
state |= StatusBarManager.DISABLE_SYSTEM_INFO;
}
@@ -2320,7 +2332,12 @@
}
private void checkBarMode(int mode, int windowState, BarTransitions transitions) {
- final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN;
+ final boolean powerSave = mBatteryController.isPowerSave();
+ final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN
+ && !powerSave;
+ if (powerSave && getBarState() != StatusBarState.KEYGUARD) {
+ mode = MODE_WARNING;
+ }
transitions.transitionTo(mode, anim);
}
@@ -2335,7 +2352,8 @@
@Override
public void run() {
checkBarModes();
- }};
+ }
+ };
@Override
public void setInteracting(int barWindow, boolean interacting) {
@@ -2654,6 +2672,9 @@
if (mUserSwitcherController != null) {
mUserSwitcherController.dump(fd, pw, args);
}
+ if (mBatteryController != null) {
+ mBatteryController.dump(fd, pw, args);
+ }
}
private String hunStateToString(Entry entry) {
@@ -2713,13 +2734,18 @@
dismissKeyguardThenExecute(new OnDismissAction() {
@Override
public boolean onDismiss() {
- try {
- // Dismiss the lock screen when Settings starts.
- ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
- } catch (RemoteException e) {
- }
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ AsyncTask.execute(new Runnable() {
+ public void run() {
+ try {
+ intent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mContext.startActivityAsUser(
+ intent, new UserHandle(UserHandle.USER_CURRENT));
+ mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
+ } catch (RemoteException e) {
+ }
+ }
+ });
animateCollapsePanels();
return DELAY_DISMISS_TO_ACTIVITY_LAUNCH;
@@ -2783,9 +2809,20 @@
};
@Override
- protected void dismissKeyguardThenExecute(OnDismissAction action) {
+ protected void dismissKeyguardThenExecute(final OnDismissAction action) {
if (mStatusBarKeyguardViewManager.isShowing()) {
- mStatusBarKeyguardViewManager.dismissWithAction(action);
+ if (UnlockMethodCache.getInstance(mContext).isMethodInsecure()
+ && mNotificationPanel.isLaunchTransitionRunning()) {
+ action.onDismiss();
+ mNotificationPanel.setLaunchTransitionEndRunnable(new Runnable() {
+ @Override
+ public void run() {
+ mStatusBarKeyguardViewManager.dismiss();
+ }
+ });
+ } else {
+ mStatusBarKeyguardViewManager.dismissWithAction(action);
+ }
} else {
action.onDismiss();
}
@@ -3121,6 +3158,8 @@
int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
"translucent".equals(mode) ? MODE_TRANSLUCENT :
"semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
+ "transparent".equals(mode) ? MODE_TRANSPARENT :
+ "warning".equals(mode) ? MODE_WARNING :
-1;
if (barMode != -1) {
boolean animate = true;
@@ -3156,6 +3195,52 @@
mLeaveOpenOnKeyguardHide = false;
}
+ public boolean isInLaunchTransition() {
+ return mNotificationPanel.isLaunchTransitionRunning()
+ || mNotificationPanel.isLaunchTransitionFinished();
+ }
+
+ /**
+ * Fades the content of the keyguard away after the launch transition is done.
+ *
+ * @param beforeFading the runnable to be run when the circle is fully expanded and the fading
+ * starts
+ * @param endRunnable the runnable to be run when the transition is done
+ */
+ public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading,
+ final Runnable endRunnable) {
+ Runnable hideRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mLaunchTransitionFadingAway = true;
+ if (beforeFading != null) {
+ beforeFading.run();
+ }
+ mNotificationPanel.setAlpha(1);
+ mNotificationPanel.animate()
+ .alpha(0)
+ .setStartDelay(FADE_KEYGUARD_START_DELAY)
+ .setDuration(FADE_KEYGUARD_DURATION)
+ .withLayer()
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mNotificationPanel.setAlpha(1);
+ if (endRunnable != null) {
+ endRunnable.run();
+ }
+ mLaunchTransitionFadingAway = false;
+ }
+ });
+ }
+ };
+ if (mNotificationPanel.isLaunchTransitionRunning()) {
+ mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
+ } else {
+ hideRunnable.run();
+ }
+ }
+
public void hideKeyguard() {
setBarState(StatusBarState.SHADE);
if (mLeaveOpenOnKeyguardHide) {
@@ -3190,8 +3275,8 @@
private void updatePublicMode() {
setLockscreenPublicMode(
- (mStatusBarKeyguardViewManager.isShowing() ||
- mStatusBarKeyguardViewManager.isOccluded())
+ (mStatusBarKeyguardViewManager.isShowing() ||
+ mStatusBarKeyguardViewManager.isOccluded())
&& mStatusBarKeyguardViewManager.isSecure());
}
@@ -3223,6 +3308,8 @@
checkBarModes();
updateCarrierLabelVisibility(false);
updateMediaMetaData(false);
+ mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
+ mStatusBarKeyguardViewManager.isSecure());
}
private void updateDozingState() {
@@ -3299,7 +3386,7 @@
private void showBouncer() {
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
- mWaitingForKeyguardExit = true;
+ mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
mStatusBarKeyguardViewManager.dismiss();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index a599070..c2fd24c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -36,13 +36,13 @@
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.TetheringController;
+import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.volume.VolumeComponent;
import java.util.ArrayList;
import java.util.List;
@@ -57,21 +57,21 @@
private final RotationLockController mRotation;
private final NetworkController mNetwork;
private final ZenModeController mZen;
- private final TetheringController mTethering;
+ private final HotspotController mHotspot;
private final CastController mCast;
private final Looper mLooper;
private final CurrentUserTracker mUserTracker;
- private final VolumeComponent mVolume;
private final ArrayList<QSTile<?>> mTiles = new ArrayList<QSTile<?>>();
private final FlashlightController mFlashlight;
private final UserSwitcherController mUserSwitcherController;
+ private final KeyguardMonitor mKeyguard;
public QSTileHost(Context context, PhoneStatusBar statusBar,
BluetoothController bluetooth, LocationController location,
RotationLockController rotation, NetworkController network,
- ZenModeController zen, TetheringController tethering,
- CastController cast, VolumeComponent volume, FlashlightController flashlight,
- UserSwitcherController userSwitcher) {
+ ZenModeController zen, HotspotController hotspot,
+ CastController cast, FlashlightController flashlight,
+ UserSwitcherController userSwitcher, KeyguardMonitor keyguard) {
mContext = context;
mStatusBar = statusBar;
mBluetooth = bluetooth;
@@ -79,11 +79,11 @@
mRotation = rotation;
mNetwork = network;
mZen = zen;
- mTethering = tethering;
+ mHotspot = hotspot;
mCast = cast;
- mVolume = volume;
mFlashlight = flashlight;
mUserSwitcherController = userSwitcher;
+ mKeyguard = keyguard;
final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName());
ht.start();
@@ -95,10 +95,10 @@
mTiles.add(new CellularTile(this));
mTiles.add(new AirplaneModeTile(this));
mTiles.add(new RotationLockTile(this));
+ mTiles.add(new FlashlightTile(this));
mTiles.add(new LocationTile(this));
mTiles.add(new CastTile(this));
mTiles.add(new HotspotTile(this));
- mTiles.add(new FlashlightTile(this));
mUserTracker = new CurrentUserTracker(mContext) {
@Override
@@ -167,8 +167,8 @@
}
@Override
- public TetheringController getTetheringController() {
- return mTethering;
+ public HotspotController getHotspotController() {
+ return mHotspot;
}
@Override
@@ -177,13 +177,13 @@
}
@Override
- public VolumeComponent getVolumeComponent() {
- return mVolume;
+ public FlashlightController getFlashlightController() {
+ return mFlashlight;
}
@Override
- public FlashlightController getFlashlightController() {
- return mFlashlight;
+ public KeyguardMonitor getKeyguardMonitor() {
+ return mKeyguard;
}
public UserSwitcherController getUserSwitcherController() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index c464f31..eb42401 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -44,12 +44,13 @@
private static final int TAG_KEY_ANIM = R.id.scrim;
private static final int NUM_TEASES = 3;
- private static final long TEASE_IN_ANIMATION_DURATION = 500;
- private static final long TEASE_VISIBLE_DURATION = 3000;
+ private static final long TEASE_IN_ANIMATION_DURATION = 1000;
+ private static final long TEASE_VISIBLE_DURATION = 2000;
private static final long TEASE_OUT_ANIMATION_DURATION = 1000;
private static final long TEASE_INVISIBLE_DURATION = 1000;
private static final long TEASE_DURATION = TEASE_IN_ANIMATION_DURATION
+ TEASE_VISIBLE_DURATION + TEASE_OUT_ANIMATION_DURATION + TEASE_INVISIBLE_DURATION;
+ private static final long PRE_TEASE_DELAY = 1000;
private final View mScrimBehind;
private final View mScrimInFront;
@@ -70,7 +71,6 @@
private boolean mAnimationStarted;
private boolean mDozing;
private int mTeasesRemaining;
-
private final Interpolator mInterpolator = new DecelerateInterpolator();
public ScrimController(View scrimBehind, View scrimInFront) {
@@ -128,8 +128,8 @@
public long tease() {
if (!mDozing) return 0;
mTeasesRemaining = NUM_TEASES;
- mScrimInFront.post(mTeaseIn);
- return NUM_TEASES * TEASE_DURATION;
+ mScrimInFront.postDelayed(mTeaseIn, PRE_TEASE_DELAY);
+ return PRE_TEASE_DELAY + NUM_TEASES * TEASE_DURATION;
}
private void cancelTeasing() {
@@ -148,7 +148,10 @@
}
private void updateScrims() {
- if ((!mKeyguardShowing && !mBouncerShowing) || mAnimateKeyguardFadingOut) {
+ if (mAnimateKeyguardFadingOut) {
+ setScrimInFrontColor(0f);
+ setScrimBehindColor(0f);
+ }else if (!mKeyguardShowing && !mBouncerShowing) {
updateScrimNormal();
setScrimInFrontColor(0);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index b2709bc..bac1d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -35,6 +35,7 @@
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.UserInfoController;
/**
@@ -97,8 +98,10 @@
private ActivityStarter mActivityStarter;
private BatteryController mBatteryController;
private QSPanel mQSPanel;
+ private boolean mHasKeyguardUserSwitcher;
private final Rect mClipBounds = new Rect();
+ private final StatusIconClipper mStatusIconClipper = new StatusIconClipper();
public StatusBarHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -231,6 +234,7 @@
updateAvatarScale();
updateClockLp();
updateBatteryLevelPaddingEnd();
+ mStatusIconClipper.update();
}
}
@@ -363,10 +367,19 @@
}
}
+ @Override
+ public void onPowerSaveChanged() {
+ // could not care less
+ }
+
private void updateClickTargets() {
setClickable(!mKeyguardShowing || mExpanded);
mDateTime.setClickable(mExpanded);
- mMultiUserSwitch.setClickable(mExpanded);
+
+ boolean keyguardSwitcherAvailable =
+ mHasKeyguardUserSwitcher && mKeyguardShowing && !mExpanded;
+ mMultiUserSwitch.setClickable(mExpanded || keyguardSwitcherAvailable);
+ mMultiUserSwitch.setKeyguardMode(keyguardSwitcherAvailable);
mSystemIconsSuperContainer.setClickable(mExpanded);
}
@@ -440,7 +453,7 @@
mSystemIconsContainer.addView(systemIcons);
mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
- mSignalCluster.addOnLayoutChangeListener(mSignalClusterChanged);
+ mSignalCluster.addOnLayoutChangeListener(mStatusIconClipper);
}
public void onSystemIconsDetached() {
@@ -448,7 +461,7 @@
mStatusIcons.setVisibility(View.VISIBLE);
}
if (mSignalCluster != null) {
- mSignalCluster.removeOnLayoutChangeListener(mSignalClusterChanged);
+ mSignalCluster.removeOnLayoutChangeListener(mStatusIconClipper);
mSignalCluster.setVisibility(View.VISIBLE);
}
mStatusIcons = null;
@@ -465,6 +478,7 @@
updateMultiUserSwitch();
updateClickTargets();
updateBatteryLevelPaddingEnd();
+ mStatusIconClipper.update();
}
public void setUserInfoController(UserInfoController userInfoController) {
@@ -501,6 +515,11 @@
mMultiUserSwitch.setQsPanel(qsp);
}
+ public void setKeyguarUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
+ mHasKeyguardUserSwitcher = true;
+ mMultiUserSwitch.setKeyguardUserSwitcher(keyguardUserSwitcher);
+ }
+
@Override
public boolean shouldDelayChildPressedState() {
return true;
@@ -514,7 +533,6 @@
}
public void setKeyguardUserSwitcherShowing(boolean showing) {
- // STOPSHIP: NOT CALLED PROPERLY WHEN GOING TO FULL SHADE AND RETURNING!?!
mKeyguardUserSwitcherShowing = showing;
updateVisibilities();
updateSystemIconsLayoutParams();
@@ -531,7 +549,7 @@
// here.
}
- private final OnLayoutChangeListener mSignalClusterChanged = new OnLayoutChangeListener() {
+ private final class StatusIconClipper implements OnLayoutChangeListener {
private final Rect mClipBounds = new Rect();
@Override
@@ -541,9 +559,15 @@
// they are mirrored to the real status bar.
mClipBounds.set(left, 0, mSystemIconsContainer.getWidth(),
mSystemIconsContainer.getHeight());
- mSystemIconsContainer.setClipBounds(mClipBounds);
+ update();
}
- };
+
+ public void update() {
+ final boolean collapsedKeyguard = mKeyguardShowing && !mExpanded;
+ final boolean expanded = mExpanded && !mOverscrolled;
+ mSystemIconsContainer.setClipBounds(collapsedKeyguard || expanded ? null : mClipBounds);
+ }
+ }
private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 93dcf90..af21f25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -176,6 +176,19 @@
}
public void setOccluded(boolean occluded) {
+ if (occluded && !mOccluded && mShowing) {
+ if (mPhoneStatusBar.isInLaunchTransition()) {
+ mOccluded = true;
+ mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
+ new Runnable() {
+ @Override
+ public void run() {
+ mStatusBarWindowManager.setKeyguardOccluded(true);
+ }
+ });
+ return;
+ }
+ }
mOccluded = occluded;
mStatusBarWindowManager.setKeyguardOccluded(occluded);
reset();
@@ -188,29 +201,50 @@
/**
* Hides the keyguard view
*/
- public void hide(long startTime, long fadeoutDuration) {
+ public void hide(long startTime, final long fadeoutDuration) {
mShowing = false;
long uptimeMillis = SystemClock.uptimeMillis();
- long delay = startTime - uptimeMillis;
- if (delay < 0) {
- delay = 0;
+ long delay = Math.max(0, startTime - uptimeMillis);
+
+ if (mPhoneStatusBar.isInLaunchTransition() ) {
+ mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
+ @Override
+ public void run() {
+ mStatusBarWindowManager.setKeyguardShowing(false);
+ mStatusBarWindowManager.setKeyguardFadingAway(true);
+ mBouncer.animateHide(PhoneStatusBar.FADE_KEYGUARD_START_DELAY,
+ PhoneStatusBar.FADE_KEYGUARD_DURATION);
+ updateStates();
+ mScrimController.animateKeyguardFadingOut(
+ PhoneStatusBar.FADE_KEYGUARD_START_DELAY,
+ PhoneStatusBar.FADE_KEYGUARD_DURATION, null);
+ }
+ }, new Runnable() {
+ @Override
+ public void run() {
+ mPhoneStatusBar.hideKeyguard();
+ mStatusBarWindowManager.setKeyguardFadingAway(false);
+ mViewMediatorCallback.keyguardGone();
+ }
+ });
+ } else {
+ mPhoneStatusBar.setKeyguardFadingAway(delay, fadeoutDuration);
+ mPhoneStatusBar.hideKeyguard();
+ mStatusBarWindowManager.setKeyguardFadingAway(true);
+ mStatusBarWindowManager.setKeyguardShowing(false);
+ mBouncer.animateHide(delay, fadeoutDuration);
+ mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() {
+ @Override
+ public void run() {
+ mStatusBarWindowManager.setKeyguardFadingAway(false);
+ mPhoneStatusBar.finishKeyguardFadingAway();
+ }
+ });
+ mViewMediatorCallback.keyguardGone();
+ updateStates();
}
- mPhoneStatusBar.setKeyguardFadingAway(delay, fadeoutDuration);
- mPhoneStatusBar.hideKeyguard();
- mStatusBarWindowManager.setKeyguardFadingAway(true);
- mStatusBarWindowManager.setKeyguardShowing(false);
- mBouncer.animateHide(delay, fadeoutDuration);
- mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() {
- @Override
- public void run() {
- mStatusBarWindowManager.setKeyguardFadingAway(false);
- mPhoneStatusBar.finishKeyguardFadingAway();
- }
- });
- mViewMediatorCallback.keyguardGone();
- updateStates();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
new file mode 100644
index 0000000..7f27a0c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import com.android.systemui.R;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.view.WindowManager;
+
+/**
+ * Base class for dialogs that should appear over panels and keyguard.
+ */
+public class SystemUIDialog extends AlertDialog {
+
+ public SystemUIDialog(Context context) {
+ super(context, R.style.Theme_SystemUI_Dialog);
+
+ getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ attrs.setTitle("SystemUIDialog");
+ getWindow().setAttributes(attrs);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index ccfe18d..1e65543 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -16,41 +16,49 @@
package com.android.systemui.statusbar.policy;
-import com.android.internal.app.IBatteryStats;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.StatusBarHeaderView;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
-import android.os.BatteryStats;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.text.format.Formatter;
+import android.os.PowerManager;
import android.util.Log;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
public class BatteryController extends BroadcastReceiver {
- private static final String TAG = "StatusBar.BatteryController";
+ private static final String TAG = "BatteryController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
+ private final ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
+ private final PowerManager mPowerManager;
private int mLevel;
private boolean mPluggedIn;
private boolean mCharging;
private boolean mCharged;
-
- public interface BatteryStateChangeCallback {
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);
- }
+ private boolean mPowerSave;
public BatteryController(Context context) {
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
context.registerReceiver(this, filter);
+
+ updatePowerSave();
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("BatteryController state:");
+ pw.print(" mLevel="); pw.println(mLevel);
+ pw.print(" mPluggedIn="); pw.println(mPluggedIn);
+ pw.print(" mCharging="); pw.println(mCharging);
+ pw.print(" mCharged="); pw.println(mCharged);
+ pw.print(" mPowerSave="); pw.println(mPowerSave);
}
public void addStateChangedCallback(BatteryStateChangeCallback cb) {
@@ -75,9 +83,40 @@
mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;
- for (BatteryStateChangeCallback cb : mChangeCallbacks) {
- cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
- }
+ fireBatteryLevelChanged();
+ } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
+ updatePowerSave();
}
}
+
+ public boolean isPowerSave() {
+ return mPowerSave;
+ }
+
+ private void updatePowerSave() {
+ final boolean powerSave = mPowerManager.isPowerSaveMode();
+ if (powerSave == mPowerSave) return;
+ mPowerSave = powerSave;
+ if (DEBUG) Log.d(TAG, "Power save is " + (mPowerSave ? "on" : "off"));
+ firePowerSaveChanged();
+ }
+
+ private void fireBatteryLevelChanged() {
+ final int N = mChangeCallbacks.size();
+ for (int i = 0; i < N; i++) {
+ mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
+ }
+ }
+
+ private void firePowerSaveChanged() {
+ final int N = mChangeCallbacks.size();
+ for (int i = 0; i < N; i++) {
+ mChangeCallbacks.get(i).onPowerSaveChanged();
+ }
+ }
+
+ public interface BatteryStateChangeCallback {
+ void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);
+ void onPowerSaveChanged();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index b059043..36cfb86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -28,6 +28,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
+import android.os.SystemProperties;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
@@ -41,6 +42,14 @@
public class FlashlightController {
private static final String TAG = "FlashlightController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final boolean ENFORCE_AVAILABILITY_LISTENER =
+ SystemProperties.getBoolean("persist.sysui.flash.listener", false);
+
+ private static final int DISPATCH_ERROR = 0;
+ private static final int DISPATCH_OFF = 1;
+ private static final int DISPATCH_AVAILABILITY_CHANGED = 2;
private final CameraManager mCameraManager;
/** Call {@link #ensureHandler()} before using */
@@ -52,6 +61,8 @@
/** Lock on {@code this} when accessing */
private boolean mFlashlightEnabled;
+ private String mCameraId;
+ private boolean mCameraAvailable;
private CameraDevice mCameraDevice;
private CaptureRequest mFlashlightRequest;
private CameraCaptureSession mSession;
@@ -60,6 +71,20 @@
public FlashlightController(Context mContext) {
mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ initialize();
+ }
+
+ public void initialize() {
+ try {
+ mCameraId = getCameraId();
+ } catch (Throwable e) {
+ Log.e(TAG, "Couldn't initialize.", e);
+ return;
+ }
+
+ ensureHandler();
+ mCameraAvailable = true;
+ mCameraManager.addAvailabilityListener(mAvailabilityListener, mHandler);
}
public synchronized void setFlashlight(boolean enabled) {
@@ -69,12 +94,18 @@
}
}
- public boolean isAvailable() {
- try {
- return getCameraId() != null;
- } catch (CameraAccessException e) {
- return false;
+ public void killFlashlight() {
+ boolean enabled;
+ synchronized (this) {
+ enabled = mFlashlightEnabled;
}
+ if (enabled) {
+ mHandler.post(mKillFlashlightRunnable);
+ }
+ }
+
+ public synchronized boolean isAvailable() {
+ return ENFORCE_AVAILABILITY_LISTENER ? mCameraAvailable : (mCameraId != null);
}
public void addListener(FlashlightListener l) {
@@ -207,24 +238,30 @@
}
private void dispatchOff() {
- dispatchListeners(false, true /* off */);
+ dispatchListeners(DISPATCH_OFF, false /* argument (ignored) */);
}
private void dispatchError() {
- dispatchListeners(true /* error */, false);
+ dispatchListeners(DISPATCH_ERROR, false /* argument (ignored) */);
}
- private void dispatchListeners(boolean error, boolean off) {
+ private void dispatchAvailabilityChanged(boolean available) {
+ dispatchListeners(DISPATCH_AVAILABILITY_CHANGED, available);
+ }
+
+ private void dispatchListeners(int message, boolean argument) {
synchronized (mListeners) {
final int N = mListeners.size();
boolean cleanup = false;
for (int i = 0; i < N; i++) {
FlashlightListener l = mListeners.get(i).get();
if (l != null) {
- if (error) {
+ if (message == DISPATCH_ERROR) {
l.onFlashlightError();
- } else if (off) {
+ } else if (message == DISPATCH_OFF) {
l.onFlashlightOff();
+ } else if (message == DISPATCH_AVAILABILITY_CHANGED) {
+ l.onFlashlightAvailabilityChanged(argument);
}
} else {
cleanup = true;
@@ -293,6 +330,48 @@
}
};
+ private final Runnable mKillFlashlightRunnable = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (this) {
+ mFlashlightEnabled = false;
+ }
+ updateFlashlight(true /* forceDisable */);
+ dispatchOff();
+ }
+ };
+
+ private final CameraManager.AvailabilityListener mAvailabilityListener =
+ new CameraManager.AvailabilityListener() {
+ @Override
+ public void onCameraAvailable(String cameraId) {
+ if (DEBUG) Log.d(TAG, "onCameraAvailable(" + cameraId + ")");
+ if (cameraId.equals(mCameraId)) {
+ setCameraAvailable(true);
+ }
+ }
+
+ @Override
+ public void onCameraUnavailable(String cameraId) {
+ if (DEBUG) Log.d(TAG, "onCameraUnavailable(" + cameraId + ")");
+ if (cameraId.equals(mCameraId)) {
+ setCameraAvailable(false);
+ }
+ }
+
+ private void setCameraAvailable(boolean available) {
+ boolean changed;
+ synchronized (FlashlightController.this) {
+ changed = mCameraAvailable != available;
+ mCameraAvailable = available;
+ }
+ if (changed) {
+ if (DEBUG) Log.d(TAG, "dispatchAvailabilityChanged(" + available + ")");
+ dispatchAvailabilityChanged(available);
+ }
+ }
+ };
+
public interface FlashlightListener {
/**
@@ -304,5 +383,11 @@
* Called when there is an error that turns the flashlight off.
*/
void onFlashlightError();
+
+ /**
+ * Called when there is a change in availability of the flashlight functionality
+ * @param available true if the flashlight is currently available.
+ */
+ void onFlashlightAvailabilityChanged(boolean available);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TetheringController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/TetheringController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
index 143ebaa..7ca91a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TetheringController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
@@ -16,13 +16,14 @@
package com.android.systemui.statusbar.policy;
-public interface TetheringController {
+public interface HotspotController {
void addCallback(Callback callback);
void removeCallback(Callback callback);
boolean isHotspotEnabled();
boolean isHotspotSupported();
+ void setHotspotEnabled(boolean enabled);
public interface Callback {
- void onHotspotChanged(boolean hotspot);
+ void onHotspotChanged(boolean enabled);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
new file mode 100644
index 0000000..eea8396
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import java.util.ArrayList;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.os.UserHandle;
+import android.util.Log;
+
+public class HotspotControllerImpl implements HotspotController {
+
+ private static final String TAG = "HotspotController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+ private final Receiver mReceiver = new Receiver();
+ private final Context mContext;
+ private final WifiManager mWifiManager;
+ private final ConnectivityManager mConnectivityManager;
+
+ public HotspotControllerImpl(Context context) {
+ mContext = context;
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ mConnectivityManager = (ConnectivityManager)
+ mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ public void addCallback(Callback callback) {
+ if (callback == null || mCallbacks.contains(callback)) return;
+ if (DEBUG) Log.d(TAG, "addCallback " + callback);
+ mCallbacks.add(callback);
+ mReceiver.setListening(!mCallbacks.isEmpty());
+ }
+
+ public void removeCallback(Callback callback) {
+ if (callback == null) return;
+ if (DEBUG) Log.d(TAG, "removeCallback " + callback);
+ mCallbacks.remove(callback);
+ mReceiver.setListening(!mCallbacks.isEmpty());
+ }
+
+ @Override
+ public boolean isHotspotEnabled() {
+ return mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED;
+ }
+
+ @Override
+ public boolean isHotspotSupported() {
+ final boolean isSecondaryUser = ActivityManager.getCurrentUser() != UserHandle.USER_OWNER;
+ return !isSecondaryUser && mConnectivityManager.isTetheringSupported();
+ }
+
+ @Override
+ public void setHotspotEnabled(boolean enabled) {
+ mWifiManager.setWifiApEnabled(null, enabled);
+ }
+
+ private void fireCallback(boolean isEnabled) {
+ for (Callback callback : mCallbacks) {
+ callback.onHotspotChanged(isEnabled);
+ }
+ }
+
+ private final class Receiver extends BroadcastReceiver {
+ private boolean mRegistered;
+
+ public void setListening(boolean listening) {
+ if (listening && !mRegistered) {
+ if (DEBUG) Log.d(TAG, "Registering receiver");
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ mContext.registerReceiver(this, filter);
+ mRegistered = true;
+ } else if (!listening && mRegistered) {
+ if (DEBUG) Log.d(TAG, "Unregistering receiver");
+ mContext.unregisterReceiver(this);
+ mRegistered = false;
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) Log.d(TAG, "onReceive " + intent.getAction());
+ fireCallback(isHotspotEnabled());
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
new file mode 100644
index 0000000..0b3575f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import java.util.ArrayList;
+
+public final class KeyguardMonitor {
+
+ private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+
+ private boolean mShowing;
+ private boolean mSecure;
+
+ public void addCallback(Callback callback) {
+ mCallbacks.add(callback);
+ }
+
+ public void removeCallback(Callback callback) {
+ mCallbacks.remove(callback);
+ }
+
+ public boolean isShowing() {
+ return mShowing;
+ }
+
+ public boolean isSecure() {
+ return mSecure;
+ }
+
+ public void notifyKeyguardState(boolean showing, boolean secure) {
+ if (mShowing == showing && mSecure == secure) return;
+ mShowing = showing;
+ mSecure = secure;
+ for (Callback callback : mCallbacks) {
+ callback.onKeyguardChanged();
+ }
+ }
+
+ public interface Callback {
+ void onKeyguardChanged();
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index a3f3819..b0bab48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -16,62 +16,56 @@
package com.android.systemui.statusbar.policy;
-import com.android.systemui.BitmapHelper;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.StatusBarHeaderView;
import com.android.systemui.statusbar.phone.UserAvatarView;
-import android.app.ActivityManagerNative;
import android.content.Context;
-import android.content.pm.UserInfo;
-import android.graphics.Bitmap;
-import android.os.AsyncTask;
-import android.os.RemoteException;
-import android.os.UserManager;
-import android.util.Log;
+import android.database.DataSetObserver;
+import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
-import android.view.WindowManagerGlobal;
import android.widget.TextView;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Manages the user switcher on the Keyguard.
*/
-public class KeyguardUserSwitcher implements View.OnClickListener {
+public class KeyguardUserSwitcher {
private static final String TAG = "KeyguardUserSwitcher";
+ private static final boolean ALWAYS_ON = false;
+ private static final String SIMPLE_USER_SWITCHER_GLOBAL_SETTING =
+ "lockscreenSimpleUserSwitcher";
- private final Context mContext;
private final ViewGroup mUserSwitcher;
- private final UserManager mUserManager;
private final StatusBarHeaderView mHeader;
+ private final Adapter mAdapter;
+ private final boolean mSimpleUserSwitcher;
public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
- StatusBarHeaderView header) {
- mContext = context;
- if (context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher)) {
+ StatusBarHeaderView header, UserSwitcherController userSwitcherController) {
+ if (context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON) {
mUserSwitcher = (ViewGroup) userSwitcher.inflate();
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mHeader = header;
- refresh();
+ mHeader.setKeyguarUserSwitcher(this);
+ mAdapter = new Adapter(context, userSwitcherController);
+ mAdapter.registerDataSetObserver(mDataSetObserver);
+ mSimpleUserSwitcher = Settings.Global.getInt(context.getContentResolver(),
+ SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0;
} else {
mUserSwitcher = null;
- mUserManager = null;
mHeader = null;
+ mAdapter = null;
+ mSimpleUserSwitcher = false;
}
}
public void setKeyguard(boolean keyguard) {
if (mUserSwitcher != null) {
- // TODO: Cache showUserSwitcherOnKeyguard().
- if (keyguard && showUserSwitcherOnKeyguard()) {
+ if (keyguard && shouldExpandByDefault()) {
show();
- refresh();
} else {
hide();
}
@@ -79,24 +73,11 @@
}
/**
- * @return true if the user switcher should be shown on the lock screen.
+ * @return true if the user switcher should be expanded by default on the lock screen.
* @see android.os.UserManager#isUserSwitcherEnabled()
*/
- private boolean showUserSwitcherOnKeyguard() {
- // TODO: Set isEdu. The edu provisioning process can add settings to Settings.Global.
- boolean isEdu = false;
- if (isEdu) {
- return true;
- }
- List<UserInfo> users = mUserManager.getUsers(true /* excludeDying */);
- int N = users.size();
- int switchableUsers = 0;
- for (int i = 0; i < N; i++) {
- if (users.get(i).supportsSwitchTo()) {
- switchableUsers++;
- }
- }
- return switchableUsers > 1;
+ private boolean shouldExpandByDefault() {
+ return mSimpleUserSwitcher || mAdapter.getSwitchableUsers() > 1;
}
public void show() {
@@ -116,100 +97,76 @@
}
private void refresh() {
- if (mUserSwitcher != null) {
- new AsyncTask<Void, Void, ArrayList<UserData>>() {
- @Override
- protected ArrayList<UserData> doInBackground(Void... params) {
- return loadUsers();
- }
-
- @Override
- protected void onPostExecute(ArrayList<UserData> userInfos) {
- bind(userInfos);
- }
- }.execute((Void[]) null);
- }
- }
-
- private void bind(ArrayList<UserData> userList) {
- mUserSwitcher.removeAllViews();
- int N = userList.size();
+ final int childCount = mUserSwitcher.getChildCount();
+ final int adapterCount = mAdapter.getCount();
+ final int N = Math.max(childCount, adapterCount);
for (int i = 0; i < N; i++) {
- mUserSwitcher.addView(inflateUser(userList.get(i)));
- }
- // TODO: add Guest
- // TODO: add (+) button
- }
-
- private View inflateUser(UserData user) {
- View v = LayoutInflater.from(mUserSwitcher.getContext()).inflate(
- R.layout.keyguard_user_switcher_item, mUserSwitcher, false);
- TextView name = (TextView) v.findViewById(R.id.name);
- UserAvatarView picture = (UserAvatarView) v.findViewById(R.id.picture);
- name.setText(user.userInfo.name);
- picture.setActivated(user.isCurrent);
- if (user.userInfo.isGuest()) {
- picture.setDrawable(mContext.getResources().getDrawable(R.drawable.ic_account_circle));
- } else {
- picture.setBitmap(user.userIcon);
- }
- v.setOnClickListener(this);
- v.setTag(user.userInfo);
- // TODO: mark which user is current for accessibility.
- return v;
- }
-
- @Override
- public void onClick(View v) {
- switchUser(((UserInfo)v.getTag()).id);
- }
-
- // TODO: Factor out logic below and share with QS implementation.
-
- private ArrayList<UserData> loadUsers() {
- ArrayList<UserInfo> users = (ArrayList<UserInfo>) mUserManager
- .getUsers(true /* excludeDying */);
- int N = users.size();
- ArrayList<UserData> result = new ArrayList<>(N);
- int currentUser = -1;
- try {
- currentUser = ActivityManagerNative.getDefault().getCurrentUser().id;
- } catch (RemoteException e) {
- Log.e(TAG, "Couln't get current user.", e);
- }
- final int avatarSize
- = mContext.getResources().getDimensionPixelSize(R.dimen.max_avatar_size);
- for (int i = 0; i < N; i++) {
- UserInfo user = users.get(i);
- if (user.supportsSwitchTo()) {
- boolean isCurrent = user.id == currentUser;
- final Bitmap picture = BitmapHelper.createCircularClip(
- mUserManager.getUserIcon(user.id),
- avatarSize, avatarSize);
- result.add(new UserData(user, picture, isCurrent));
+ if (i < adapterCount) {
+ View oldView = null;
+ if (i < childCount) {
+ oldView = mUserSwitcher.getChildAt(i);
+ }
+ View newView = mAdapter.getView(i, oldView, mUserSwitcher);
+ if (oldView == null) {
+ // We ran out of existing views. Add it at the end.
+ mUserSwitcher.addView(newView);
+ } else if (oldView != newView) {
+ // We couldn't rebind the view. Replace it.
+ mUserSwitcher.removeViewAt(i);
+ mUserSwitcher.addView(newView, i);
+ }
+ } else {
+ int lastIndex = mUserSwitcher.getChildCount() - 1;
+ mUserSwitcher.removeViewAt(lastIndex);
}
}
- return result;
}
- private void switchUser(int userId) {
- try {
- WindowManagerGlobal.getWindowManagerService().lockNow(null);
- ActivityManagerNative.getDefault().switchUser(userId);
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't switch user.", e);
+ public final DataSetObserver mDataSetObserver = new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ refresh();
}
- }
+ };
- private static class UserData {
- final UserInfo userInfo;
- final Bitmap userIcon;
- final boolean isCurrent;
+ public static class Adapter extends UserSwitcherController.BaseUserAdapter implements
+ View.OnClickListener {
- UserData(UserInfo userInfo, Bitmap userIcon, boolean isCurrent) {
- this.userInfo = userInfo;
- this.userIcon = userIcon;
- this.isCurrent = isCurrent;
+ private Context mContext;
+
+ public Adapter(Context context, UserSwitcherController controller) {
+ super(controller);
+ mContext = context;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ UserSwitcherController.UserRecord item = getItem(position);
+
+ if (convertView == null
+ || !(convertView.getTag() instanceof UserSwitcherController.UserRecord)) {
+ convertView = LayoutInflater.from(mContext).inflate(
+ R.layout.keyguard_user_switcher_item, parent, false);
+ convertView.setOnClickListener(this);
+ }
+
+ TextView nameView = (TextView) convertView.findViewById(R.id.name);
+ UserAvatarView pictureView = (UserAvatarView) convertView.findViewById(R.id.picture);
+
+ nameView.setText(getName(mContext, item));
+ if (item.picture == null) {
+ pictureView.setDrawable(mContext.getDrawable(R.drawable.ic_account_circle_qs));
+ } else {
+ pictureView.setBitmap(item.picture);
+ }
+ convertView.setActivated(item.isCurrent);
+ convertView.setTag(item);
+ return convertView;
+ }
+
+ @Override
+ public void onClick(View v) {
+ switchTo(((UserSwitcherController.UserRecord)v.getTag()));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 2134042..7c00c7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -17,14 +17,18 @@
package com.android.systemui.statusbar.policy;
import com.android.systemui.BitmapHelper;
+import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.tiles.UserDetailView;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
+import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
@@ -52,15 +56,21 @@
public class UserSwitcherController {
private static final String TAG = "UserSwitcherController";
+ private static final boolean DEBUG = false;
private final Context mContext;
private final UserManager mUserManager;
private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
+ private final GuestResumeSessionReceiver mGuestResumeSessionReceiver
+ = new GuestResumeSessionReceiver();
private ArrayList<UserRecord> mUsers = new ArrayList<>();
+ private Dialog mExitGuestDialog;
+ private int mLastNonGuestUser = UserHandle.USER_OWNER;
public UserSwitcherController(Context context) {
mContext = context;
+ mGuestResumeSessionReceiver.register(context);
mUserManager = UserManager.get(context);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
@@ -113,7 +123,7 @@
if (info.isGuest()) {
guestRecord = new UserRecord(info, null /* picture */,
true /* isGuest */, isCurrent);
- } else if (!info.isManagedProfile()) {
+ } else if (info.supportsSwitchTo()) {
Bitmap picture = bitmaps.get(info.id);
if (picture == null) {
picture = mUserManager.getUserIcon(info.id);
@@ -169,7 +179,7 @@
if (ActivityManager.getCurrentUser() == id) {
if (record.isGuest) {
- exitGuest(id);
+ showExitGuestDialog(id);
}
return;
}
@@ -186,16 +196,39 @@
}
}
+ private void showExitGuestDialog(int id) {
+ if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
+ mExitGuestDialog.cancel();
+ }
+ mExitGuestDialog = new ExitGuestDialog(mContext, id);
+ mExitGuestDialog.show();
+ }
+
private void exitGuest(int id) {
- // TODO: show confirmation dialog
- switchToUserId(UserHandle.USER_OWNER);
+ int newId = UserHandle.USER_OWNER;
+ if (mLastNonGuestUser != UserHandle.USER_OWNER) {
+ UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
+ if (info != null && info.isEnabled() && info.supportsSwitchTo()) {
+ newId = info.id;
+ }
+ }
+ switchToUserId(newId);
mUserManager.removeUser(id);
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Log.v(TAG, "Broadcast: a=" + intent.getAction()
+ + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
+ }
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
+ if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
+ mExitGuestDialog.cancel();
+ mExitGuestDialog = null;
+ }
+
final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
final int N = mUsers.size();
for (int i = 0; i < N; i++) {
@@ -205,6 +238,9 @@
if (record.isCurrent != shouldBeCurrent) {
mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent));
}
+ if (shouldBeCurrent && !record.isGuest) {
+ mLastNonGuestUser = record.info.id;
+ }
}
notifyAdapters();
}
@@ -219,6 +255,7 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("UserSwitcherController state:");
+ pw.println(" mLastNonGuestUser=" + mLastNonGuestUser);
pw.print(" mUsers.size="); pw.println(mUsers.size());
for (int i = 0; i < mUsers.size(); i++) {
final UserRecord u = mUsers.get(i);
@@ -266,6 +303,18 @@
return item.info.name;
}
}
+
+ public int getSwitchableUsers() {
+ int result = 0;
+ ArrayList<UserRecord> users = mController.mUsers;
+ int N = users.size();
+ for (int i = 0; i < N; i++) {
+ if (users.get(i).info != null) {
+ result++;
+ }
+ }
+ return result;
+ }
}
public static final class UserRecord {
@@ -341,4 +390,32 @@
public void setToggleState(boolean state) {
}
};
+
+ private final class ExitGuestDialog extends SystemUIDialog implements
+ DialogInterface.OnClickListener {
+
+ private final int mGuestId;
+
+ public ExitGuestDialog(Context context, int guestId) {
+ super(context);
+ setTitle(R.string.guest_exit_guest_dialog_title);
+ setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ context.getString(android.R.string.no), this);
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getString(android.R.string.yes), this);
+ setCanceledOnTouchOutside(false);
+ mGuestId = guestId;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == BUTTON_NEGATIVE) {
+ cancel();
+ } else {
+ dismiss();
+ exitGuest(mGuestId);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index ee7206f..fe2733b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -171,7 +171,10 @@
for (int i = 0; i < childCount; i++) {
View child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
- childViewState.belowSpeedBump = speedBumpIndex != -1 && i > speedBumpIndex;
+
+ // The speed bump can also be gone, so equality needs to be taken when comparing
+ // indices.
+ childViewState.belowSpeedBump = speedBumpIndex != -1 && i >= speedBumpIndex;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 7abfc88..f5447a2 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -65,7 +65,6 @@
final AlertController.AlertParams ap = mAlertParams;
ap.mTitle = getString(R.string.usb_debugging_title);
- ap.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
ap.mMessage = getString(R.string.usb_debugging_message, fingerprints);
ap.mPositiveButtonText = getString(android.R.string.ok);
ap.mNegativeButtonText = getString(android.R.string.cancel);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 9ba9745..b85fbf3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -371,7 +371,7 @@
final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
- listenToRingerMode();
+ registerReceiver();
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -441,9 +441,10 @@
updateStates();
}
- private void listenToRingerMode() {
+ private void registerReceiver() {
final IntentFilter filter = new IntentFilter();
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -453,6 +454,10 @@
removeMessages(MSG_RINGER_MODE_CHANGED);
sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
}
+
+ if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ postDismiss(0);
+ }
}
}, filter);
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 71f1d211..44a5344 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2339,8 +2339,8 @@
Settings.System.putIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, brightness,
UserHandle.USER_CURRENT_OR_SELF);
- Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ mContext.startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG),
+ UserHandle.CURRENT_OR_SELF);
}
return -1;
} else if (keyCode == KeyEvent.KEYCODE_META_LEFT) {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 17a5263..56b8c92 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -6409,54 +6409,61 @@
mIsSystemRestore = isFullSystemRestore;
mFinished = false;
- if (filterSet == null) {
- // We want everything and a pony
- List<PackageInfo> apps
- = PackageManagerBackupAgent.getStorableApplications(mPackageManager);
- filterSet = packagesToNames(apps);
- if (DEBUG) {
- Slog.i(TAG, "Full restore; asking for " + filterSet.length + " apps");
- }
- }
-
- mAcceptSet = new ArrayList<PackageInfo>(filterSet.length);
-
- // Pro tem, we insist on moving the settings provider package to last place.
- // Keep track of whether it's in the list, and bump it down if so. We also
- // want to do the system package itself first if it's called for.
- boolean hasSystem = false;
- boolean hasSettings = false;
- for (int i = 0; i < filterSet.length; i++) {
- try {
- PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0);
- if ("android".equals(info.packageName)) {
- hasSystem = true;
- continue;
+ if (targetPackage != null) {
+ // Single package restore
+ mAcceptSet = new ArrayList<PackageInfo>();
+ mAcceptSet.add(targetPackage);
+ } else {
+ // Everything possible, or a target set
+ if (filterSet == null) {
+ // We want everything and a pony
+ List<PackageInfo> apps =
+ PackageManagerBackupAgent.getStorableApplications(mPackageManager);
+ filterSet = packagesToNames(apps);
+ if (DEBUG) {
+ Slog.i(TAG, "Full restore; asking for " + filterSet.length + " apps");
}
- if (SETTINGS_PACKAGE.equals(info.packageName)) {
- hasSettings = true;
- continue;
- }
+ }
- if (appIsEligibleForBackup(info.applicationInfo)) {
- mAcceptSet.add(info);
+ mAcceptSet = new ArrayList<PackageInfo>(filterSet.length);
+
+ // Pro tem, we insist on moving the settings provider package to last place.
+ // Keep track of whether it's in the list, and bump it down if so. We also
+ // want to do the system package itself first if it's called for.
+ boolean hasSystem = false;
+ boolean hasSettings = false;
+ for (int i = 0; i < filterSet.length; i++) {
+ try {
+ PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0);
+ if ("android".equals(info.packageName)) {
+ hasSystem = true;
+ continue;
+ }
+ if (SETTINGS_PACKAGE.equals(info.packageName)) {
+ hasSettings = true;
+ continue;
+ }
+
+ if (appIsEligibleForBackup(info.applicationInfo)) {
+ mAcceptSet.add(info);
+ }
+ } catch (NameNotFoundException e) {
+ // requested package name doesn't exist; ignore it
}
- } catch (NameNotFoundException e) {
- // requested package name doesn't exist; ignore it
}
- }
- if (hasSystem) {
- try {
- mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0));
- } catch (NameNotFoundException e) {
- // won't happen; we know a priori that it's valid
+ if (hasSystem) {
+ try {
+ mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0));
+ } catch (NameNotFoundException e) {
+ // won't happen; we know a priori that it's valid
+ }
}
- }
- if (hasSettings) {
- try {
- mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0));
- } catch (NameNotFoundException e) {
- // this one is always valid too
+ if (hasSettings) {
+ try {
+ mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0));
+ } catch (NameNotFoundException e) {
+ // this one is always valid too
+ }
}
}
@@ -6558,117 +6565,77 @@
private void startRestore() {
sendStartRestore(mAcceptSet.size());
- UnifiedRestoreState nextState = UnifiedRestoreState.RESTORE_FINISHED;
try {
- // If we don't yet have PM metadata for this token, synthesize an
- // entry for the PM pseudopackage and make it the first to be
- // restored.
String transportDir = mTransport.transportDirName();
mStateDir = new File(mBaseStateDir, transportDir);
- File metadataDir = new File(mStateDir, "_metadata");
- metadataDir.mkdirs();
- File metadataFile = new File(metadataDir, Long.toHexString(mToken));
- try {
- // PM info is cached in $BASE/transport/_metadata/$TOKEN
- mPmAgent = new PackageManagerBackupAgent(metadataFile);
- } catch (IOException e) {
- // Nope, we need to get it via restore
- if (MORE_DEBUG) Slog.v(TAG, "Need to restore @pm@");
- PackageInfo pmPackage = new PackageInfo();
- pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
- mAcceptSet.add(0, pmPackage);
- }
+
+ // Fetch the current metadata from the dataset first
+ PackageInfo pmPackage = new PackageInfo();
+ pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
+ mAcceptSet.add(0, pmPackage);
PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]);
mStatus = mTransport.startRestore(mToken, packages);
if (mStatus != BackupTransport.TRANSPORT_OK) {
Slog.e(TAG, "Transport error " + mStatus + "; no restore possible");
mStatus = BackupTransport.TRANSPORT_ERROR;
- nextState = UnifiedRestoreState.FINAL;
+ executeNextState(UnifiedRestoreState.FINAL);
return;
}
- if (mPmAgent == null) {
- if (DEBUG) {
- Slog.v(TAG, "Need to fetch metadata for token "
- + Long.toHexString(mToken));
- }
- RestoreDescription desc = mTransport.nextRestorePackage();
- if (desc == null) {
- Slog.e(TAG, "No restore metadata available; halting");
- mStatus = BackupTransport.TRANSPORT_ERROR;
- nextState = UnifiedRestoreState.FINAL;
- return;
- }
- if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) {
- Slog.e(TAG, "Required metadata but got " + desc.getPackageName());
- mStatus = BackupTransport.TRANSPORT_ERROR;
- nextState = UnifiedRestoreState.FINAL;
- return;
- }
-
- // Pull the Package Manager metadata from the restore set first
- mCurrentPackage = new PackageInfo();
- mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
- mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
- mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
- if (MORE_DEBUG) {
- Slog.v(TAG, "initiating restore for PMBA");
- }
- initiateOneRestore(mCurrentPackage, 0);
- // The PM agent called operationComplete() already, because our invocation
- // of it is process-local and therefore synchronous. That means that a
- // RUNNING_QUEUE message is already enqueued. Only if we're unable to
- // proceed with running the queue do we remove that pending message and
- // jump straight to the FINAL state.
-
- // Verify that the backup set includes metadata. If not, we can't do
- // signature/version verification etc, so we simply do not proceed with
- // the restore operation.
- if (!mPmAgent.hasMetadata()) {
- Slog.e(TAG, "No restore metadata available, so not restoring settings");
- EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
- PACKAGE_MANAGER_SENTINEL,
- "Package manager restore metadata missing");
- mStatus = BackupTransport.TRANSPORT_ERROR;
- mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
- nextState = UnifiedRestoreState.FINAL;
- return;
- }
-
- // Success; cache the metadata and continue as expected with the
- // RUNNING_QUEUE step already enqueued.
- if (DEBUG) {
- Slog.v(TAG, "Got metadata; caching and proceeding to restore");
- }
- try {
- mPmAgent.saveToDisk(metadataFile);
- } catch (IOException e) {
- // Something bad; we need to abort
- Slog.e(TAG, "Unable to write restored metadata");
- EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
- PACKAGE_MANAGER_SENTINEL,
- "Unable to write restored metadata");
- mStatus = BackupTransport.TRANSPORT_ERROR;
- mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
- nextState = UnifiedRestoreState.FINAL;
- return;
- }
- } else {
- // We have the PMBA already, so we can proceed directly to
- // the RUNNING_QUEUE state ourselves.
- if (MORE_DEBUG) Slog.v(TAG, "PMBA from cache; proceeding to run queue");
- nextState = UnifiedRestoreState.RUNNING_QUEUE;
+ RestoreDescription desc = mTransport.nextRestorePackage();
+ if (desc == null) {
+ Slog.e(TAG, "No restore metadata available; halting");
+ mStatus = BackupTransport.TRANSPORT_ERROR;
+ executeNextState(UnifiedRestoreState.FINAL);
+ return;
}
+ if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) {
+ Slog.e(TAG, "Required metadata but got " + desc.getPackageName());
+ mStatus = BackupTransport.TRANSPORT_ERROR;
+ executeNextState(UnifiedRestoreState.FINAL);
+ return;
+ }
+
+ // Pull the Package Manager metadata from the restore set first
+ mCurrentPackage = new PackageInfo();
+ mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
+ mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
+ mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "initiating restore for PMBA");
+ }
+ initiateOneRestore(mCurrentPackage, 0);
+ // The PM agent called operationComplete() already, because our invocation
+ // of it is process-local and therefore synchronous. That means that the
+ // next-state message (RUNNING_QUEUE) is already enqueued. Only if we're
+ // unable to proceed with running the queue do we remove that pending
+ // message and jump straight to the FINAL state.
+
+ // Verify that the backup set includes metadata. If not, we can't do
+ // signature/version verification etc, so we simply do not proceed with
+ // the restore operation.
+ if (!mPmAgent.hasMetadata()) {
+ Slog.e(TAG, "No restore metadata available, so not restoring");
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
+ PACKAGE_MANAGER_SENTINEL,
+ "Package manager restore metadata missing");
+ mStatus = BackupTransport.TRANSPORT_ERROR;
+ mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
+ executeNextState(UnifiedRestoreState.FINAL);
+ return;
+ }
+
+ // Success; cache the metadata and continue as expected with the
+ // next state already enqueued
+
} catch (RemoteException e) {
// If we lost the transport at any time, halt
Slog.e(TAG, "Unable to contact transport for restore");
mStatus = BackupTransport.TRANSPORT_ERROR;
mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
- nextState = UnifiedRestoreState.FINAL;
+ executeNextState(UnifiedRestoreState.FINAL);
return;
- } finally {
- executeNextState(nextState);
}
}
@@ -7267,6 +7234,13 @@
final UnifiedRestoreState nextState;
switch (mState) {
+ case INITIAL:
+ // We've just (manually) restored the PMBA. It doesn't need the
+ // additional restore-finished callback so we bypass that and go
+ // directly to running the queue.
+ nextState = UnifiedRestoreState.RUNNING_QUEUE;
+ break;
+
case RESTORE_KEYVALUE:
case RESTORE_FULL: {
// Okay, we've just heard back from the agent that it's done with
@@ -8187,6 +8161,9 @@
pkg.packageName = packageName;
mWakelock.acquire();
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Restore at install of " + packageName);
+ }
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
msg.obj = new RestoreParams(transport, dirName, null,
restoreSet, pkg, token);
@@ -8368,6 +8345,9 @@
if (token == mRestoreSets[i].token) {
long oldId = Binder.clearCallingIdentity();
mWakelock.acquire();
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "restoreAll() kicking off");
+ }
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
msg.obj = new RestoreParams(mRestoreTransport, dirName,
observer, token);
@@ -8439,6 +8419,9 @@
if (token == mRestoreSets[i].token) {
long oldId = Binder.clearCallingIdentity();
mWakelock.acquire();
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "restoreSome() of " + packages.length + " packages");
+ }
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token,
packages, packages.length > 1);
@@ -8518,6 +8501,9 @@
// Ready to go: enqueue the restore request and claim success
long oldId = Binder.clearCallingIdentity();
mWakelock.acquire();
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "restorePackage() : " + packageName);
+ }
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
msg.obj = new RestoreParams(mRestoreTransport, dirName,
observer, token, app, 0);
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index f64e97f..fcca788 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -37,7 +37,6 @@
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
-import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -48,9 +47,6 @@
import java.util.Set;
import java.util.Objects;
-import java.util.Map.Entry;
-
-import libcore.io.IoUtils;
/**
* We back up the signatures of each package so that during a system restore,
@@ -132,93 +128,6 @@
mStoredIncrementalVersion = Build.VERSION.INCREMENTAL;
}
- /**
- * Reconstitute a PMBA from its on-disk format. This is used for persistence
- * of the ancestral dataset's metadata. See {@link #saveToDisk()} for
- * details of the file format.
- */
- PackageManagerBackupAgent(File cache) throws IOException {
- FileInputStream fin = new FileInputStream(cache);
- BufferedInputStream bin = new BufferedInputStream(fin, 32 * 1024);
- DataInputStream in = new DataInputStream(bin);
-
- int version = in.readInt();
- // We can currently only handle the initial version format
- if (version == ANCESTRAL_RECORD_VERSION) {
- mStoredSdkVersion = in.readInt();
- mStoredIncrementalVersion = in.readUTF();
-
- int nPackages = in.readInt();
- if (nPackages > 0) {
- HashMap<String, Metadata> restoredMetadata =
- new HashMap<String, Metadata>(nPackages);
- ArrayList<byte[]> hashes = null;
- for (int pack = 0; pack < nPackages; pack++) {
- final String name = in.readUTF();
- final int versionCode = in.readInt();
- final int nHashes = in.readInt();
- if (nHashes > 0) {
- hashes = new ArrayList<byte[]>(nHashes);
- for (int i = 0; i < nHashes; i++) {
- int len = in.readInt();
- byte[] hash = new byte[len];
- in.read(hash);
- hashes.add(hash);
- }
- }
- restoredMetadata.put(name, new Metadata(versionCode, hashes));
- }
- mRestoredSignatures = restoredMetadata;
- }
- }
- }
-
- public void saveToDisk(File cache) throws IOException {
- // On disk format is very similar to the key/value format:
- //
- // Int: disk format version, currently 1
- // Int: VERSION.SDK_INT of source device
- // UTF: VERSION.INCREMENTAL string, for reference
- //
- // Int: number of packages represented in this file
- //
- // Per package if number > 0:
- // UTF: package name
- // Int: versionCode of the package
- // Int: number of signature hash blocks for this package
- // Per signature hash block:
- // Int: size of block
- // byte[]: block itself if size of block > 0
- FileOutputStream of = new FileOutputStream(cache);
- BufferedOutputStream bout = new BufferedOutputStream(of, 32*1024);
- DataOutputStream out = new DataOutputStream(bout);
-
- out.writeInt(ANCESTRAL_RECORD_VERSION);
- out.writeInt(mStoredSdkVersion);
- out.writeUTF(mStoredIncrementalVersion);
-
- out.writeInt(mRestoredSignatures.size());
- if (mRestoredSignatures.size() > 0) {
- Set<Entry<String, Metadata>> entries = mRestoredSignatures.entrySet();
- for (Entry<String, Metadata> i : entries) {
- final Metadata m = i.getValue();
- final int nHashes = (m.sigHashes != null) ? m.sigHashes.size() : 0;
- out.writeUTF(i.getKey());
- out.writeInt(m.versionCode);
- out.writeInt(nHashes);
- for (int h = 0; h < nHashes; h++) {
- byte[] hash = m.sigHashes.get(h);
- out.writeInt(hash.length);
- if (hash.length > 0) {
- out.write(hash);
- }
- }
- }
- }
- out.flush();
- IoUtils.closeQuietly(out);
- }
-
// We will need to refresh our understanding of what is eligible for
// backup periodically; this entry point serves that purpose.
public void evaluateStorablePackages() {
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 579c449..5a510a9 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -371,11 +371,10 @@
@Override
public void setMode(int code, int uid, String packageName, int mode) {
- if (Binder.getCallingPid() == Process.myPid()) {
- return;
+ if (Binder.getCallingPid() != Process.myPid()) {
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
}
- mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
verifyIncomingOp(code);
ArrayList<Callback> repCbs = null;
code = AppOpsManager.opToSwitch(code);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9945909..36be58c 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -41,7 +41,6 @@
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import android.app.AlarmManager;
-import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -52,9 +51,7 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -224,10 +221,6 @@
AlarmManager mAlarmManager;
- // used in recursive route setting to add gateways for the host for which
- // a host route was requested.
- private static final int MAX_HOSTROUTE_CYCLE_COUNT = 10;
-
private Tethering mTethering;
private KeyStore mKeyStore;
@@ -261,11 +254,6 @@
private CaptivePortalTracker mCaptivePortalTracker;
/**
- * The link properties that define the current links
- */
- private LinkProperties mCurrentLinkProperties[];
-
- /**
* A per Net list of the PID's that requested access to the net
* used both as a refcount and for per-PID DNS selection
*/
@@ -297,15 +285,6 @@
private static final int ENABLED = 1;
private static final int DISABLED = 0;
- private static final boolean ADD = true;
- private static final boolean REMOVE = false;
-
- private static final boolean TO_DEFAULT_TABLE = true;
- private static final boolean TO_SECONDARY_TABLE = false;
-
- private static final boolean EXEMPT = true;
- private static final boolean UNEXEMPT = false;
-
/**
* used internally as a delayed event to make us switch back to the
* default network
@@ -450,19 +429,6 @@
private InetAddress mDefaultDns;
- // Lock for protecting access to mAddedRoutes and mExemptAddresses
- private final Object mRoutesLock = new Object();
-
- // this collection is used to refcount the added routes - if there are none left
- // it's time to remove the route from the route table
- @GuardedBy("mRoutesLock")
- private Collection<RouteInfo> mAddedRoutes = new ArrayList<RouteInfo>();
-
- // this collection corresponds to the entries of mAddedRoutes that have routing exemptions
- // used to handle cleanup of exempt rules
- @GuardedBy("mRoutesLock")
- private Collection<LinkAddress> mExemptAddresses = new ArrayList<LinkAddress>();
-
// used in DBG mode to track inet condition reports
private static final int INET_CONDITION_LOG_MAX_SIZE = 15;
private ArrayList mInetLog;
@@ -479,8 +445,6 @@
private SettingsObserver mSettingsObserver;
- private AppOpsManager mAppOpsManager;
-
private UserManager mUserManager;
NetworkConfig[] mNetConfigs;
@@ -514,8 +478,6 @@
// sequence number of NetworkRequests
private int mNextNetworkRequestId = 1;
- private static final int UID_UNUSED = -1;
-
/**
* Implements support for the legacy "one network per network type" model.
*
@@ -701,7 +663,6 @@
mNetTrackers = new NetworkStateTracker[
ConnectivityManager.MAX_NETWORK_TYPE+1];
- mCurrentLinkProperties = new LinkProperties[ConnectivityManager.MAX_NETWORK_TYPE+1];
mRadioAttributes = new RadioAttributes[ConnectivityManager.MAX_RADIO_TYPE+1];
mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1];
@@ -827,7 +788,7 @@
}
}
- mTethering = new Tethering(mContext, mNetd, statsService, this, mHandler.getLooper());
+ mTethering = new Tethering(mContext, mNetd, statsService, mHandler.getLooper());
//set up the listener for user state for creating user VPNs
IntentFilter intentFilter = new IntentFilter();
@@ -884,8 +845,6 @@
filter.addAction(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
mContext.registerReceiver(mProvisioningReceiver, filter);
- mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
}
@@ -1710,42 +1669,6 @@
}
/**
- * Check if the address falls into any of currently running VPN's route's.
- */
- private boolean isAddressUnderVpn(InetAddress address) {
- synchronized (mVpns) {
- synchronized (mRoutesLock) {
- int uid = UserHandle.getCallingUserId();
- Vpn vpn = mVpns.get(uid);
- if (vpn == null) {
- return false;
- }
-
- // Check if an exemption exists for this address.
- for (LinkAddress destination : mExemptAddresses) {
- if (!NetworkUtils.addressTypeMatches(address, destination.getAddress())) {
- continue;
- }
-
- int prefix = destination.getPrefixLength();
- InetAddress addrMasked = NetworkUtils.getNetworkPart(address, prefix);
- InetAddress destMasked = NetworkUtils.getNetworkPart(destination.getAddress(),
- prefix);
-
- if (addrMasked.equals(destMasked)) {
- return false;
- }
- }
-
- // Finally check if the address is covered by the VPN.
- return vpn.isAddressCovered(address);
- }
- }
- }
-
- /**
- * @deprecated use requestRouteToHostAddress instead
- *
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface.
* @param networkType the type of the network over which traffic to the
@@ -1754,32 +1677,12 @@
* desired
* @return {@code true} on success, {@code false} on failure
*/
- public boolean requestRouteToHost(int networkType, int hostAddress, String packageName) {
- InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress);
-
- if (inetAddress == null) {
- return false;
- }
-
- return requestRouteToHostAddress(networkType, inetAddress.getAddress(), packageName);
- }
-
- /**
- * Ensure that a network route exists to deliver traffic to the specified
- * host via the specified network interface.
- * @param networkType the type of the network over which traffic to the
- * specified host is to be routed
- * @param hostAddress the IP address of the host to which the route is
- * desired
- * @return {@code true} on success, {@code false} on failure
- */
- public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress,
- String packageName) {
+ public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
enforceChangePermission();
if (mProtectedNetworks.contains(networkType)) {
enforceConnectivityInternalPermission();
}
- boolean exempt;
+
InetAddress addr;
try {
addr = InetAddress.getByAddress(hostAddress);
@@ -1787,26 +1690,6 @@
if (DBG) log("requestRouteToHostAddress got " + e.toString());
return false;
}
- // System apps may request routes bypassing the VPN to keep other networks working.
- if (Binder.getCallingUid() == Process.SYSTEM_UID) {
- exempt = true;
- } else {
- mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
- try {
- ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(packageName,
- 0);
- exempt = (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- } catch (NameNotFoundException e) {
- throw new IllegalArgumentException("Failed to find calling package details", e);
- }
- }
-
- // Non-exempt routeToHost's can only be added if the host is not covered by the VPN.
- // This can be either because the VPN's routes do not cover the destination or a
- // system application added an exemption that covers this destination.
- if (!exempt && isAddressUnderVpn(addr)) {
- return false;
- }
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
if (DBG) log("requestRouteToHostAddress on invalid network: " + networkType);
@@ -1822,13 +1705,13 @@
}
return false;
}
+
DetailedState netState;
synchronized (nai) {
netState = nai.networkInfo.getDetailedState();
}
- if ((netState != DetailedState.CONNECTED &&
- netState != DetailedState.CAPTIVE_PORTAL_CHECK)) {
+ if (netState != DetailedState.CONNECTED && netState != DetailedState.CAPTIVE_PORTAL_CHECK) {
if (VDBG) {
log("requestRouteToHostAddress on down network "
+ "(" + networkType + ") - dropped"
@@ -1836,6 +1719,7 @@
}
return false;
}
+
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
@@ -1845,7 +1729,7 @@
lp = nai.linkProperties;
netId = nai.network.netId;
}
- boolean ok = modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE, exempt, netId, uid);
+ boolean ok = addLegacyRouteToHost(lp, addr, netId, uid);
if (DBG) log("requestRouteToHostAddress ok=" + ok);
return ok;
} finally {
@@ -1853,17 +1737,7 @@
}
}
- private boolean addRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable,
- boolean exempt, int netId) {
- return modifyRoute(p, r, 0, ADD, toDefaultTable, exempt, netId, false, UID_UNUSED);
- }
-
- private boolean removeRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable, int netId) {
- return modifyRoute(p, r, 0, REMOVE, toDefaultTable, UNEXEMPT, netId, false, UID_UNUSED);
- }
-
- private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd,
- boolean toDefaultTable, boolean exempt, int netId, int uid) {
+ private boolean addLegacyRouteToHost(LinkProperties lp, InetAddress addr, int netId, int uid) {
RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getAllRoutes(), addr);
if (bestRoute == null) {
bestRoute = RouteInfo.makeHostRoute(addr, lp.getInterfaceName());
@@ -1878,125 +1752,14 @@
bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway(), iface);
}
}
- return modifyRoute(lp, bestRoute, 0, doAdd, toDefaultTable, exempt, netId, true, uid);
- }
-
- /*
- * TODO: Clean all this stuff up. Once we have UID-based routing, stuff will break due to
- * incorrect tracking of mAddedRoutes, so a cleanup becomes necessary and urgent. But at
- * the same time, there'll be no more need to track mAddedRoutes or mExemptAddresses,
- * or even have the concept of an exempt address, or do things like "selectBestRoute", or
- * determine "default" vs "secondary" table, etc., so the cleanup becomes possible.
- */
- private boolean modifyRoute(LinkProperties lp, RouteInfo r, int cycleCount, boolean doAdd,
- boolean toDefaultTable, boolean exempt, int netId, boolean legacy, int uid) {
- if ((lp == null) || (r == null)) {
- if (DBG) log("modifyRoute got unexpected null: " + lp + ", " + r);
+ if (VDBG) log("Adding " + bestRoute + " for interface " + bestRoute.getInterface());
+ try {
+ mNetd.addLegacyRouteForNetId(netId, bestRoute, uid);
+ } catch (Exception e) {
+ // never crash - catch them all
+ if (DBG) loge("Exception trying to add a route: " + e);
return false;
}
-
- if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
- loge("Error modifying route - too much recursion");
- return false;
- }
-
- String ifaceName = r.getInterface();
- if(ifaceName == null) {
- loge("Error modifying route - no interface name");
- return false;
- }
- if (r.hasGateway()) {
- RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getAllRoutes(), r.getGateway());
- if (bestRoute != null) {
- if (bestRoute.getGateway().equals(r.getGateway())) {
- // if there is no better route, add the implied hostroute for our gateway
- bestRoute = RouteInfo.makeHostRoute(r.getGateway(), ifaceName);
- } else {
- // if we will connect to our gateway through another route, add a direct
- // route to it's gateway
- bestRoute = RouteInfo.makeHostRoute(r.getGateway(),
- bestRoute.getGateway(),
- ifaceName);
- }
- modifyRoute(lp, bestRoute, cycleCount+1, doAdd, toDefaultTable, exempt, netId,
- legacy, uid);
- }
- }
- if (doAdd) {
- if (VDBG) log("Adding " + r + " for interface " + ifaceName);
- try {
- if (toDefaultTable) {
- synchronized (mRoutesLock) {
- // only track default table - only one apps can effect
- mAddedRoutes.add(r);
- if (legacy) {
- mNetd.addLegacyRouteForNetId(netId, r, uid);
- } else {
- mNetd.addRoute(netId, r);
- }
- if (exempt) {
- LinkAddress dest = r.getDestinationLinkAddress();
- if (!mExemptAddresses.contains(dest)) {
- mNetd.setHostExemption(dest);
- mExemptAddresses.add(dest);
- }
- }
- }
- } else {
- if (legacy) {
- mNetd.addLegacyRouteForNetId(netId, r, uid);
- } else {
- mNetd.addRoute(netId, r);
- }
- }
- } catch (Exception e) {
- // never crash - catch them all
- if (DBG) loge("Exception trying to add a route: " + e);
- return false;
- }
- } else {
- // if we remove this one and there are no more like it, then refcount==0 and
- // we can remove it from the table
- if (toDefaultTable) {
- synchronized (mRoutesLock) {
- mAddedRoutes.remove(r);
- if (mAddedRoutes.contains(r) == false) {
- if (VDBG) log("Removing " + r + " for interface " + ifaceName);
- try {
- if (legacy) {
- mNetd.removeLegacyRouteForNetId(netId, r, uid);
- } else {
- mNetd.removeRoute(netId, r);
- }
- LinkAddress dest = r.getDestinationLinkAddress();
- if (mExemptAddresses.contains(dest)) {
- mNetd.clearHostExemption(dest);
- mExemptAddresses.remove(dest);
- }
- } catch (Exception e) {
- // never crash - catch them all
- if (VDBG) loge("Exception trying to remove a route: " + e);
- return false;
- }
- } else {
- if (VDBG) log("not removing " + r + " as it's still in use");
- }
- }
- } else {
- if (VDBG) log("Removing " + r + " for interface " + ifaceName);
- try {
- if (legacy) {
- mNetd.removeLegacyRouteForNetId(netId, r, uid);
- } else {
- mNetd.removeRoute(netId, r);
- }
- } catch (Exception e) {
- // never crash - catch them all
- if (VDBG) loge("Exception trying to remove a route: " + e);
- return false;
- }
- }
- }
return true;
}
@@ -2607,6 +2370,7 @@
*
* TODO - delete when we're sure all this functionallity is captured.
*/
+ /*
private void handleConnectivityChange(int netType, LinkProperties curLp, boolean doReset) {
int resetMask = doReset ? NetworkUtils.RESET_ALL_ADDRESSES : 0;
boolean exempt = ConnectivityManager.isNetworkTypeExempt(netType);
@@ -2615,10 +2379,8 @@
+ " resetMask=" + resetMask);
}
- /*
- * If a non-default network is enabled, add the host routes that
- * will allow it's DNS servers to be accessed.
- */
+ // If a non-default network is enabled, add the host routes that
+ // will allow it's DNS servers to be accessed.
handleDnsConfigurationChange(netType);
LinkProperties newLp = null;
@@ -2719,7 +2481,7 @@
// TODO: Temporary notifying upstread change to Tethering.
// @see bug/4455071
- /** Notify TetheringService if interface name has been changed. */
+ // Notify TetheringService if interface name has been changed.
if (TextUtils.equals(mNetTrackers[netType].getNetworkInfo().getReason(),
PhoneConstants.REASON_LINK_PROPERTIES_CHANGED)) {
if (isTetheringSupported()) {
@@ -2727,6 +2489,7 @@
}
}
}
+ */
/**
* Add and remove routes using the old properties (null if not previously connected),
@@ -2736,6 +2499,7 @@
* host routes should be set to the dns servers
* returns a boolean indicating the routes changed
*/
+ /*
private boolean updateRoutes(LinkProperties newLp, LinkProperties curLp,
boolean isLinkDefault, boolean exempt, int netId) {
Collection<RouteInfo> routesToAdd = null;
@@ -2789,6 +2553,7 @@
return routesChanged;
}
+ */
/**
* Reads the network specific MTU size from reources.
@@ -3264,10 +3029,12 @@
// any activity by applications trying to use this
// connection will fail until the provisioning network
// is enabled.
+ /*
for (RouteInfo r : lp.getRoutes()) {
removeRoute(lp, r, TO_DEFAULT_TABLE,
mNetTrackers[info.getType()].getNetwork().netId);
}
+ */
} else if (state == NetworkInfo.State.DISCONNECTED) {
} else if (state == NetworkInfo.State.SUSPENDED) {
} else if (state == NetworkInfo.State.CONNECTED) {
@@ -3283,8 +3050,10 @@
// TODO: Temporary allowing network configuration
// change not resetting sockets.
// @see bug/4455071
+ /*
handleConnectivityChange(info.getType(), mCurrentLinkProperties[info.getType()],
false);
+ */
break;
}
case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: {
@@ -4683,7 +4452,7 @@
// Make a route to host so we check the specific interface.
if (mCs.requestRouteToHostAddress(ConnectivityManager.TYPE_MOBILE_HIPRI,
- hostAddr.getAddress(), null)) {
+ hostAddr.getAddress())) {
// Wait a short time to be sure the route is established ??
log("isMobileOk:"
+ " wait to establish route to hostAddr=" + hostAddr);
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index bae2d22..3a4e2ee 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -28,6 +28,7 @@
import com.android.server.location.GeofenceProxy;
import com.android.server.location.GpsLocationProvider;
import com.android.server.location.GpsMeasurementsProvider;
+import com.android.server.location.GpsNavigationMessageProvider;
import com.android.server.location.LocationBlacklist;
import com.android.server.location.LocationFudger;
import com.android.server.location.LocationProviderInterface;
@@ -60,6 +61,7 @@
import android.location.GeocoderParams;
import android.location.Geofence;
import android.location.IGpsMeasurementsListener;
+import android.location.IGpsNavigationMessageListener;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
import android.location.ILocationListener;
@@ -159,6 +161,7 @@
private PassiveProvider mPassiveProvider; // track passive provider for special cases
private LocationBlacklist mBlacklist;
private GpsMeasurementsProvider mGpsMeasurementsProvider;
+ private GpsNavigationMessageProvider mGpsNavigationMessageProvider;
// --- fields below are protected by mLock ---
// Set of providers that are explicitly enabled
@@ -409,6 +412,7 @@
mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider);
}
mGpsMeasurementsProvider = gpsProvider.getGpsMeasurementsProvider();
+ mGpsNavigationMessageProvider = gpsProvider.getGpsNavigationMessageProvider();
/*
Load package name(s) containing location provider support.
@@ -1847,7 +1851,6 @@
if (!hasLocationAccess) {
return false;
}
-
return mGpsMeasurementsProvider.addListener(listener);
}
@@ -1857,6 +1860,35 @@
}
@Override
+ public boolean addGpsNavigationMessageListener(
+ IGpsNavigationMessageListener listener,
+ String packageName) {
+ int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+ checkResolutionLevelIsSufficientForProviderUse(
+ allowedResolutionLevel,
+ LocationManager.GPS_PROVIDER);
+
+ int uid = Binder.getCallingUid();
+ long identity = Binder.clearCallingIdentity();
+ boolean hasLocationAccess;
+ try {
+ hasLocationAccess = checkLocationAccess(uid, packageName, allowedResolutionLevel);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ if (!hasLocationAccess) {
+ return false;
+ }
+ return mGpsNavigationMessageProvider.addListener(listener);
+ }
+
+ @Override
+ public boolean removeGpsNavigationMessageListener(IGpsNavigationMessageListener listener) {
+ return mGpsNavigationMessageProvider.removeListener(listener);
+ }
+
+ @Override
public boolean sendExtraCommand(String provider, String command, Bundle extras) {
if (provider == null) {
// throw NullPointerException to remain compatible with previous implementation
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 5cfc49c..86ce961 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -346,7 +346,8 @@
maybeUpdateKeystore(password, userId);
- writeFile(getLockPasswordFilename(userId), mLockPatternUtils.passwordToHash(password));
+ writeFile(getLockPasswordFilename(userId),
+ mLockPatternUtils.passwordToHash(password, userId));
}
@Override
@@ -391,7 +392,7 @@
return true;
}
// Compare the hash from the file with the entered password's hash
- final byte[] hash = mLockPatternUtils.passwordToHash(password);
+ final byte[] hash = mLockPatternUtils.passwordToHash(password, userId);
final boolean matched = Arrays.equals(stored, hash);
if (matched && !TextUtils.isEmpty(password)) {
maybeUpdateKeystore(password, userId);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 2434f712..db423b0 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -108,15 +108,6 @@
private static final String NETD_TAG = "NetdConnector";
private static final String NETD_SOCKET_NAME = "netd";
- private static final String ADD = "add";
- private static final String REMOVE = "remove";
-
- private static final String ALLOW = "allow";
- private static final String DENY = "deny";
-
- private static final String DEFAULT = "default";
- private static final String SECONDARY = "secondary";
-
private static final int MAX_UID_RANGES_PER_COMMAND = 10;
/**
@@ -126,7 +117,7 @@
public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
class NetdResponseCode {
- /* Keep in sync with system/netd/ResponseCode.h */
+ /* Keep in sync with system/netd/server/ResponseCode.h */
public static final int InterfaceListResult = 110;
public static final int TetherInterfaceListResult = 111;
public static final int TetherDnsFwdTgtListResult = 112;
@@ -946,15 +937,15 @@
@Override
public void addRoute(int netId, RouteInfo route) {
- modifyRoute(netId, ADD, route);
+ modifyRoute("add", "" + netId, route);
}
@Override
public void removeRoute(int netId, RouteInfo route) {
- modifyRoute(netId, REMOVE, route);
+ modifyRoute("remove", "" + netId, route);
}
- private void modifyRoute(int netId, String action, RouteInfo route) {
+ private void modifyRoute(String action, String netId, RouteInfo route) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
final Command cmd = new Command("network", "route", action, netId);
@@ -1175,6 +1166,11 @@
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
+ List<RouteInfo> routes = new ArrayList<RouteInfo>();
+ // The RouteInfo constructor truncates the LinkAddress to a network prefix, thus making it
+ // suitable to use as a route destination.
+ routes.add(new RouteInfo(getInterfaceConfig(iface).getLinkAddress(), null, iface));
+ addInterfaceToLocalNetwork(iface, routes);
}
@Override
@@ -1185,6 +1181,7 @@
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
+ removeInterfaceFromLocalNetwork(iface);
}
@Override
@@ -1748,36 +1745,6 @@
}
@Override
- public void setHostExemption(LinkAddress host) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute("interface", "fwmark", "exempt", "add", host);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
- @Override
- public void clearHostExemption(LinkAddress host) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute("interface", "fwmark", "exempt", "remove", host);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
- @Override
- public void flushNetworkDnsCache(int netId) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute("resolver", "flushnet", netId);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
- @Override
public void setFirewallEnabled(boolean enabled) {
enforceSystemUid();
try {
@@ -1798,7 +1765,7 @@
public void setFirewallInterfaceRule(String iface, boolean allow) {
enforceSystemUid();
Preconditions.checkState(mFirewallEnabled);
- final String rule = allow ? ALLOW : DENY;
+ final String rule = allow ? "allow" : "deny";
try {
mConnector.execute("firewall", "set_interface_rule", iface, rule);
} catch (NativeDaemonConnectorException e) {
@@ -1810,7 +1777,7 @@
public void setFirewallEgressSourceRule(String addr, boolean allow) {
enforceSystemUid();
Preconditions.checkState(mFirewallEnabled);
- final String rule = allow ? ALLOW : DENY;
+ final String rule = allow ? "allow" : "deny";
try {
mConnector.execute("firewall", "set_egress_source_rule", addr, rule);
} catch (NativeDaemonConnectorException e) {
@@ -1822,7 +1789,7 @@
public void setFirewallEgressDestRule(String addr, int port, boolean allow) {
enforceSystemUid();
Preconditions.checkState(mFirewallEnabled);
- final String rule = allow ? ALLOW : DENY;
+ final String rule = allow ? "allow" : "deny";
try {
mConnector.execute("firewall", "set_egress_dest_rule", addr, port, rule);
} catch (NativeDaemonConnectorException e) {
@@ -1834,7 +1801,7 @@
public void setFirewallUidRule(int uid, boolean allow) {
enforceSystemUid();
Preconditions.checkState(mFirewallEnabled);
- final String rule = allow ? ALLOW : DENY;
+ final String rule = allow ? "allow" : "deny";
try {
mConnector.execute("firewall", "set_uid_rule", uid, rule);
} catch (NativeDaemonConnectorException e) {
@@ -2003,21 +1970,18 @@
@Override
public void addInterfaceToNetwork(String iface, int netId) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-
- try {
- mConnector.execute("network", "addiface", netId, iface);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
+ modifyInterfaceInNetwork("add", "" + netId, iface);
}
@Override
public void removeInterfaceFromNetwork(String iface, int netId) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ modifyInterfaceInNetwork("remove", "" + netId, iface);
+ }
+ private void modifyInterfaceInNetwork(String action, String netId, String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("network", "removeiface", netId, iface);
+ mConnector.execute("network", "interface", action, netId, iface);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
@@ -2025,18 +1989,9 @@
@Override
public void addLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) {
- modifyLegacyRouteForNetId(netId, routeInfo, uid, ADD);
- }
-
- @Override
- public void removeLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) {
- modifyLegacyRouteForNetId(netId, routeInfo, uid, REMOVE);
- }
-
- private void modifyLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid, String action) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- final Command cmd = new Command("network", "route", "legacy", uid, action, netId);
+ final Command cmd = new Command("network", "route", "legacy", uid, "add", netId);
// create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr
final LinkAddress la = routeInfo.getDestinationLinkAddress();
@@ -2131,4 +2086,19 @@
}
}
+ @Override
+ public void addInterfaceToLocalNetwork(String iface, List<RouteInfo> routes) {
+ modifyInterfaceInNetwork("add", "local", iface);
+
+ for (RouteInfo route : routes) {
+ if (!route.isDefaultRoute()) {
+ modifyRoute("add", "local", route);
+ }
+ }
+ }
+
+ @Override
+ public void removeInterfaceFromLocalNetwork(String iface) {
+ modifyInterfaceInNetwork("remove", "local", iface);
+ }
}
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index fe97c71..cf7e65c 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -603,7 +603,7 @@
return mUniqueId;
}
- /* These should be in sync with system/netd/mDnsResponseCode.h */
+ /* These should be in sync with system/netd/server/ResponseCode.h */
class NativeResponseCode {
public static final int SERVICE_DISCOVERY_FAILED = 602;
public static final int SERVICE_FOUND = 603;
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 36d67ee..9a86136 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -77,6 +77,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.FgThread;
+
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
@@ -109,7 +110,7 @@
private static final int TIMEOUT_DELAY_MS = 1000 * 60;
private static final String DATABASE_NAME = "accounts.db";
- private static final int DATABASE_VERSION = 5;
+ private static final int DATABASE_VERSION = 6;
private final Context mContext;
@@ -130,6 +131,7 @@
private static final String ACCOUNTS_TYPE = "type";
private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
private static final String ACCOUNTS_PASSWORD = "password";
+ private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
private static final String TABLE_AUTHTOKENS = "authtokens";
private static final String AUTHTOKENS_ID = "_id";
@@ -196,6 +198,20 @@
/** protected by the {@link #cacheLock} */
private final HashMap<Account, HashMap<String, String>> authTokenCache =
new HashMap<Account, HashMap<String, String>>();
+ /**
+ * protected by the {@link #cacheLock}
+ *
+ * Caches the previous names associated with an account. Previous names
+ * should be cached because we expect that when an Account is renamed,
+ * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
+ * want to know if the accounts they care about have been renamed.
+ *
+ * The previous names are wrapped in an {@link AtomicReference} so that
+ * we can distinguish between those accounts with no previous names and
+ * those whose previous names haven't been cached (yet).
+ */
+ private final HashMap<Account, AtomicReference<String>> previousNameCache =
+ new HashMap<Account, AtomicReference<String>>();
UserAccounts(Context context, int userId) {
this.userId = userId;
@@ -517,6 +533,57 @@
}
@Override
+ public String getPreviousName(Account account) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "getPreviousName: " + account
+ + ", caller's uid " + Binder.getCallingUid()
+ + ", pid " + Binder.getCallingPid());
+ }
+ if (account == null) throw new IllegalArgumentException("account is null");
+ UserAccounts accounts = getUserAccountsForCaller();
+ long identityToken = clearCallingIdentity();
+ try {
+ return readPreviousNameInternal(accounts, account);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private String readPreviousNameInternal(UserAccounts accounts, Account account) {
+ if (account == null) {
+ return null;
+ }
+ synchronized (accounts.cacheLock) {
+ AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
+ if (previousNameRef == null) {
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+ Cursor cursor = db.query(
+ TABLE_ACCOUNTS,
+ new String[]{ ACCOUNTS_PREVIOUS_NAME },
+ ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+ new String[] { account.name, account.type },
+ null,
+ null,
+ null);
+ try {
+ if (cursor.moveToNext()) {
+ String previousName = cursor.getString(0);
+ previousNameRef = new AtomicReference<String>(previousName);
+ accounts.previousNameCache.put(account, previousNameRef);
+ return previousName;
+ } else {
+ return null;
+ }
+ } finally {
+ cursor.close();
+ }
+ } else {
+ return previousNameRef.get();
+ }
+ }
+ }
+
+ @Override
public String getUserData(Account account, String key) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getUserData: " + account
@@ -859,6 +926,119 @@
}
@Override
+ public void renameAccount(
+ IAccountManagerResponse response, Account accountToRename, String newName) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
+ + ", caller's uid " + Binder.getCallingUid()
+ + ", pid " + Binder.getCallingPid());
+ }
+ if (accountToRename == null) throw new IllegalArgumentException("account is null");
+ checkAuthenticateAccountsPermission(accountToRename);
+ UserAccounts accounts = getUserAccountsForCaller();
+ long identityToken = clearCallingIdentity();
+ try {
+ Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
+ Bundle result = new Bundle();
+ result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
+ result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
+ try {
+ response.onResult(result);
+ } catch (RemoteException e) {
+ Log.w(TAG, e.getMessage());
+ }
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private Account renameAccountInternal(
+ UserAccounts accounts, Account accountToRename, String newName) {
+ Account resultAccount = null;
+ /*
+ * Cancel existing notifications. Let authenticators
+ * re-post notifications as required. But we don't know if
+ * the authenticators have bound their notifications to
+ * now stale account name data.
+ *
+ * With a rename api, we might not need to do this anymore but it
+ * shouldn't hurt.
+ */
+ cancelNotification(
+ getSigninRequiredNotificationId(accounts, accountToRename),
+ new UserHandle(accounts.userId));
+ synchronized(accounts.credentialsPermissionNotificationIds) {
+ for (Pair<Pair<Account, String>, Integer> pair:
+ accounts.credentialsPermissionNotificationIds.keySet()) {
+ if (accountToRename.equals(pair.first.first)) {
+ int id = accounts.credentialsPermissionNotificationIds.get(pair);
+ cancelNotification(id, new UserHandle(accounts.userId));
+ }
+ }
+ }
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ db.beginTransaction();
+ boolean isSuccessful = false;
+ Account renamedAccount = new Account(newName, accountToRename.type);
+ try {
+ final ContentValues values = new ContentValues();
+ values.put(ACCOUNTS_NAME, newName);
+ values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
+ final long accountId = getAccountIdLocked(db, accountToRename);
+ if (accountId >= 0) {
+ final String[] argsAccountId = { String.valueOf(accountId) };
+ db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
+ db.setTransactionSuccessful();
+ isSuccessful = true;
+ }
+ } finally {
+ db.endTransaction();
+ if (isSuccessful) {
+ /*
+ * Database transaction was successful. Clean up cached
+ * data associated with the account in the user profile.
+ */
+ insertAccountIntoCacheLocked(accounts, renamedAccount);
+ /*
+ * Extract the data and token caches before removing the
+ * old account to preserve the user data associated with
+ * the account.
+ */
+ HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
+ HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
+ removeAccountFromCacheLocked(accounts, accountToRename);
+ /*
+ * Update the cached data associated with the renamed
+ * account.
+ */
+ accounts.userDataCache.put(renamedAccount, tmpData);
+ accounts.authTokenCache.put(renamedAccount, tmpTokens);
+ accounts.previousNameCache.put(
+ renamedAccount,
+ new AtomicReference<String>(accountToRename.name));
+ resultAccount = renamedAccount;
+
+ if (accounts.userId == UserHandle.USER_OWNER) {
+ /*
+ * Owner's account was renamed, rename the account for
+ * those users with which the account was shared.
+ */
+ List<UserInfo> users = mUserManager.getUsers(true);
+ for (UserInfo user : users) {
+ if (!user.isPrimary() && user.isRestricted()) {
+ renameSharedAccountAsUser(accountToRename, newName, user.id);
+ }
+ }
+ }
+ sendAccountsChangedBroadcast(accounts.userId);
+ }
+ }
+ }
+ return resultAccount;
+ }
+
+ @Override
public void removeAccount(IAccountManagerResponse response, Account account) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "removeAccount: " + account
@@ -2061,6 +2241,26 @@
}
@Override
+ public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
+ userId = handleIncomingUser(userId);
+ UserAccounts accounts = getUserAccounts(userId);
+ SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final ContentValues values = new ContentValues();
+ values.put(ACCOUNTS_NAME, newName);
+ values.put(ACCOUNTS_PREVIOUS_NAME, account.name);
+ int r = db.update(
+ TABLE_SHARED_ACCOUNTS,
+ values,
+ ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+ new String[] { account.name, account.type });
+ if (r > 0) {
+ // Recursively rename the account.
+ renameAccountInternal(accounts, account, newName);
+ }
+ return r > 0;
+ }
+
+ @Override
public boolean removeSharedAccountAsUser(Account account, int userId) {
userId = handleIncomingUser(userId);
UserAccounts accounts = getUserAccounts(userId);
@@ -2557,6 +2757,7 @@
+ ACCOUNTS_NAME + " TEXT NOT NULL, "
+ ACCOUNTS_TYPE + " TEXT NOT NULL, "
+ ACCOUNTS_PASSWORD + " TEXT, "
+ + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
+ "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
@@ -2592,6 +2793,10 @@
+ "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
}
+ private void addOldAccountNameColumn(SQLiteDatabase db) {
+ db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
+ }
+
private void createAccountsDeletionTrigger(SQLiteDatabase db) {
db.execSQL(""
+ " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
@@ -2642,6 +2847,11 @@
oldVersion++;
}
+ if (oldVersion == 5) {
+ addOldAccountNameColumn(db);
+ oldVersion++;
+ }
+
if (oldVersion != newVersion) {
Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
}
@@ -3050,6 +3260,7 @@
}
accounts.userDataCache.remove(account);
accounts.authTokenCache.remove(account);
+ accounts.previousNameCache.remove(account);
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3aaa402..fed68f9 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -45,8 +46,8 @@
import android.service.voice.IVoiceInteractionSession;
import android.util.ArrayMap;
import android.util.ArraySet;
-
import android.util.SparseIntArray;
+
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
@@ -221,7 +222,11 @@
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
+
private static final String USER_DATA_DIR = "/data/user/";
+ // File that stores last updated system version and called preboot receivers
+ static final String CALLED_PRE_BOOTS_FILENAME = "called_pre_boots.dat";
+
static final String TAG = "ActivityManager";
static final String TAG_MU = "ActivityManagerServiceMU";
static final boolean DEBUG = false;
@@ -353,6 +358,8 @@
static final int ALLOW_NON_FULL_IN_PROFILE = 1;
static final int ALLOW_FULL_ONLY = 2;
+ static final int LAST_PREBOOT_DELIVERED_FILE_VERSION = 10000;
+
/** All system services */
SystemServiceManager mSystemServiceManager;
@@ -3545,6 +3552,32 @@
}
}
+ @Override
+ public final int startActivityFromRecents(int taskId, Bundle options) {
+ if (checkCallingPermission(START_TASKS_FROM_RECENTS) != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: startActivityFromRecents called without " +
+ START_TASKS_FROM_RECENTS;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ final int callingUid;
+ final String callingPackage;
+ final Intent intent;
+ final int userId;
+ synchronized (this) {
+ final TaskRecord task = recentTaskForIdLocked(taskId);
+ if (task == null) {
+ throw new ActivityNotFoundException("Task " + taskId + " not found.");
+ }
+ callingUid = task.mCallingUid;
+ callingPackage = task.mCallingPackage;
+ intent = task.intent;
+ userId = task.userId;
+ }
+ return startActivityInPackage(callingUid, callingPackage, intent, null, null, null, 0, 0,
+ options, userId, null);
+ }
+
final int startActivityInPackage(int uid, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, Bundle options, int userId,
@@ -6536,13 +6569,20 @@
}
void grantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
- final int modeFlags, UriPermissionOwner owner) {
+ final int modeFlags, UriPermissionOwner owner, int targetUserId) {
if (targetPkg == null) {
throw new NullPointerException("targetPkg");
}
+ int targetUid;
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ try {
+ targetUid = pm.getPackageUid(targetPkg, targetUserId);
+ } catch (RemoteException ex) {
+ return;
+ }
- int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, modeFlags,
- -1);
+ targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, modeFlags,
+ targetUid);
if (targetUid < 0) {
return;
}
@@ -6693,7 +6733,8 @@
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
- grantUriPermissionLocked(r.uid, targetPkg, grantUri, modeFlags, null);
+ grantUriPermissionLocked(r.uid, targetPkg, grantUri, modeFlags, null,
+ UserHandle.getUserId(r.uid));
}
}
@@ -6870,7 +6911,7 @@
@Override
public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, Uri uri,
- final int modeFlags, int userId) {
+ final int modeFlags, int sourceUserId, int targetUserId) {
synchronized(this) {
UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
if (owner == null) {
@@ -6890,8 +6931,8 @@
throw new IllegalArgumentException("null uri");
}
- grantUriPermissionLocked(fromUid, targetPkg, new GrantUri(userId, uri, false),
- modeFlags, owner);
+ grantUriPermissionLocked(fromUid, targetPkg, new GrantUri(sourceUserId, uri, false),
+ modeFlags, owner, targetUserId);
}
}
@@ -8042,8 +8083,8 @@
boolean checkedGrants = false;
if (checkUser) {
// Looking for cross-user grants before enforcing the typical cross-users permissions
- int tmpTargetUserId = unsafeConvertIncomingUser(UserHandle.getUserId(callingUid));
- if (tmpTargetUserId != userId) {
+ int tmpTargetUserId = unsafeConvertIncomingUser(userId);
+ if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
if (checkAuthorityGrants(callingUid, cpi, tmpTargetUserId, checkUser)) {
return null;
}
@@ -9912,12 +9953,10 @@
private static File getCalledPreBootReceiversFile() {
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
- File fname = new File(systemDir, "called_pre_boots.dat");
+ File fname = new File(systemDir, CALLED_PRE_BOOTS_FILENAME);
return fname;
}
- static final int LAST_DONE_VERSION = 10000;
-
private static ArrayList<ComponentName> readLastDonePreBootReceivers() {
ArrayList<ComponentName> lastDoneReceivers = new ArrayList<ComponentName>();
File file = getCalledPreBootReceiversFile();
@@ -9926,7 +9965,7 @@
fis = new FileInputStream(file);
DataInputStream dis = new DataInputStream(new BufferedInputStream(fis, 2048));
int fvers = dis.readInt();
- if (fvers == LAST_DONE_VERSION) {
+ if (fvers == LAST_PREBOOT_DELIVERED_FILE_VERSION) {
String vers = dis.readUTF();
String codename = dis.readUTF();
String build = dis.readUTF();
@@ -9955,16 +9994,15 @@
}
return lastDoneReceivers;
}
-
+
private static void writeLastDonePreBootReceivers(ArrayList<ComponentName> list) {
File file = getCalledPreBootReceiversFile();
FileOutputStream fos = null;
DataOutputStream dos = null;
try {
- Slog.i(TAG, "Writing new set of last done pre-boot receivers...");
fos = new FileOutputStream(file);
dos = new DataOutputStream(new BufferedOutputStream(fos, 2048));
- dos.writeInt(LAST_DONE_VERSION);
+ dos.writeInt(LAST_PREBOOT_DELIVERED_FILE_VERSION);
dos.writeUTF(android.os.Build.VERSION.RELEASE);
dos.writeUTF(android.os.Build.VERSION.CODENAME);
dos.writeUTF(android.os.Build.VERSION.INCREMENTAL);
@@ -9988,11 +10026,92 @@
}
}
}
-
+
+ private boolean deliverPreBootCompleted(final Runnable onFinishCallback,
+ ArrayList<ComponentName> doneReceivers, int userId) {
+ boolean waitingUpdate = false;
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ List<ResolveInfo> ris = null;
+ try {
+ ris = AppGlobals.getPackageManager().queryIntentReceivers(
+ intent, null, 0, userId);
+ } catch (RemoteException e) {
+ }
+ if (ris != null) {
+ for (int i=ris.size()-1; i>=0; i--) {
+ if ((ris.get(i).activityInfo.applicationInfo.flags
+ &ApplicationInfo.FLAG_SYSTEM) == 0) {
+ ris.remove(i);
+ }
+ }
+ intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
+
+ // For User 0, load the version number. When delivering to a new user, deliver
+ // to all receivers.
+ if (userId == UserHandle.USER_OWNER) {
+ ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
+ for (int i=0; i<ris.size(); i++) {
+ ActivityInfo ai = ris.get(i).activityInfo;
+ ComponentName comp = new ComponentName(ai.packageName, ai.name);
+ if (lastDoneReceivers.contains(comp)) {
+ // We already did the pre boot receiver for this app with the current
+ // platform version, so don't do it again...
+ ris.remove(i);
+ i--;
+ // ...however, do keep it as one that has been done, so we don't
+ // forget about it when rewriting the file of last done receivers.
+ doneReceivers.add(comp);
+ }
+ }
+ }
+
+ // If primary user, send broadcast to all available users, else just to userId
+ final int[] users = userId == UserHandle.USER_OWNER ? getUsersLocked()
+ : new int[] { userId };
+ for (int i = 0; i < ris.size(); i++) {
+ ActivityInfo ai = ris.get(i).activityInfo;
+ ComponentName comp = new ComponentName(ai.packageName, ai.name);
+ doneReceivers.add(comp);
+ intent.setComponent(comp);
+ for (int j=0; j<users.length; j++) {
+ IIntentReceiver finisher = null;
+ // On last receiver and user, set up a completion callback
+ if (i == ris.size() - 1 && j == users.length - 1 && onFinishCallback != null) {
+ finisher = new IIntentReceiver.Stub() {
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ // The raw IIntentReceiver interface is called
+ // with the AM lock held, so redispatch to
+ // execute our code without the lock.
+ mHandler.post(onFinishCallback);
+ }
+ };
+ }
+ Slog.i(TAG, "Sending system update to " + intent.getComponent()
+ + " for user " + users[j]);
+ broadcastIntentLocked(null, null, intent, null, finisher,
+ 0, null, null, null, AppOpsManager.OP_NONE,
+ true, false, MY_PID, Process.SYSTEM_UID,
+ users[j]);
+ if (finisher != null) {
+ waitingUpdate = true;
+ }
+ }
+ }
+ }
+
+ return waitingUpdate;
+ }
+
public void systemReady(final Runnable goingCallback) {
synchronized(this) {
if (mSystemReady) {
- if (goingCallback != null) goingCallback.run();
+ // If we're done calling all the receivers, run the next "boot phase" passed in
+ // by the SystemServer
+ if (goingCallback != null) {
+ goingCallback.run();
+ }
return;
}
@@ -10013,82 +10132,20 @@
if (mWaitingUpdate) {
return;
}
- Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
- List<ResolveInfo> ris = null;
- try {
- ris = AppGlobals.getPackageManager().queryIntentReceivers(
- intent, null, 0, 0);
- } catch (RemoteException e) {
- }
- if (ris != null) {
- for (int i=ris.size()-1; i>=0; i--) {
- if ((ris.get(i).activityInfo.applicationInfo.flags
- &ApplicationInfo.FLAG_SYSTEM) == 0) {
- ris.remove(i);
+ final ArrayList<ComponentName> doneReceivers = new ArrayList<ComponentName>();
+ mWaitingUpdate = deliverPreBootCompleted(new Runnable() {
+ public void run() {
+ synchronized (ActivityManagerService.this) {
+ mDidUpdate = true;
}
+ writeLastDonePreBootReceivers(doneReceivers);
+ showBootMessage(mContext.getText(
+ R.string.android_upgrading_complete),
+ false);
+ systemReady(goingCallback);
}
- intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
+ }, doneReceivers, UserHandle.USER_OWNER);
- ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
-
- final ArrayList<ComponentName> doneReceivers = new ArrayList<ComponentName>();
- for (int i=0; i<ris.size(); i++) {
- ActivityInfo ai = ris.get(i).activityInfo;
- ComponentName comp = new ComponentName(ai.packageName, ai.name);
- if (lastDoneReceivers.contains(comp)) {
- // We already did the pre boot receiver for this app with the current
- // platform version, so don't do it again...
- ris.remove(i);
- i--;
- // ...however, do keep it as one that has been done, so we don't
- // forget about it when rewriting the file of last done receivers.
- doneReceivers.add(comp);
- }
- }
-
- final int[] users = getUsersLocked();
- for (int i=0; i<ris.size(); i++) {
- ActivityInfo ai = ris.get(i).activityInfo;
- ComponentName comp = new ComponentName(ai.packageName, ai.name);
- doneReceivers.add(comp);
- intent.setComponent(comp);
- for (int j=0; j<users.length; j++) {
- IIntentReceiver finisher = null;
- if (i == ris.size()-1 && j == users.length-1) {
- finisher = new IIntentReceiver.Stub() {
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) {
- // The raw IIntentReceiver interface is called
- // with the AM lock held, so redispatch to
- // execute our code without the lock.
- mHandler.post(new Runnable() {
- public void run() {
- synchronized (ActivityManagerService.this) {
- mDidUpdate = true;
- }
- writeLastDonePreBootReceivers(doneReceivers);
- showBootMessage(mContext.getText(
- R.string.android_upgrading_complete),
- false);
- systemReady(goingCallback);
- }
- });
- }
- };
- }
- Slog.i(TAG, "Sending system update to " + intent.getComponent()
- + " for user " + users[j]);
- broadcastIntentLocked(null, null, intent, null, finisher,
- 0, null, null, null, AppOpsManager.OP_NONE,
- true, false, MY_PID, Process.SYSTEM_UID,
- users[j]);
- if (finisher != null) {
- mWaitingUpdate = true;
- }
- }
- }
- }
if (mWaitingUpdate) {
return;
}
@@ -17121,6 +17178,7 @@
}
if (needStart) {
+ // Send USER_STARTED broadcast
Intent intent = new Intent(Intent.ACTION_USER_STARTED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
@@ -17132,6 +17190,11 @@
if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
if (userId != UserHandle.USER_OWNER) {
+ // Send PRE_BOOT_COMPLETED broadcasts for this new user
+ final ArrayList<ComponentName> doneReceivers
+ = new ArrayList<ComponentName>();
+ deliverPreBootCompleted(null, doneReceivers, userId);
+
Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntentLocked(null, null, intent, null,
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index c6dbfba..690b90a 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3284,10 +3284,11 @@
mLockTaskNotify = new LockTaskNotify(mService.mContext);
}
mLockTaskNotify.show(true);
+ mLockTaskIsLocked = msg.arg2 == 0;
if (getStatusBarService() != null) {
int flags =
StatusBarManager.DISABLE_MASK ^ StatusBarManager.DISABLE_BACK;
- if (msg.arg2 != 0) {
+ if (!mLockTaskIsLocked) {
flags ^= StatusBarManager.DISABLE_HOME
| StatusBarManager.DISABLE_RECENT;
}
@@ -3323,7 +3324,7 @@
boolean shouldLockKeyguard = Settings.System.getInt(
mService.mContext.getContentResolver(),
Settings.System.LOCK_TO_APP_EXIT_LOCKED) != 0;
- if (shouldLockKeyguard) {
+ if (!mLockTaskIsLocked && shouldLockKeyguard) {
mWindowManager.lockNow(null);
mWindowManager.dismissKeyguard();
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ac19bde..3fdeb54 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -413,6 +413,20 @@
}
}
+ public void noteResetAudio() {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteResetAudioLocked();
+ }
+ }
+
+ public void noteResetVideo() {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteResetVideoLocked();
+ }
+ }
+
public void noteFlashlightOn() {
enforceCallingPermission();
synchronized (mStats) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 061053a..d6f922f 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -68,10 +68,14 @@
private static final String ATTR_TASK_AFFILIATION = "task_affiliation";
private static final String ATTR_PREV_AFFILIATION = "prev_affiliation";
private static final String ATTR_NEXT_AFFILIATION = "next_affiliation";
+ private static final String ATTR_CALLING_UID = "calling_uid";
+ private static final String ATTR_CALLING_PACKAGE = "calling_package";
private static final String LAST_ACTIVITY_ICON_SUFFIX = "_last_activity_icon_";
private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
+ static final boolean IGNORE_RETURN_TO_RECENTS = true;
+
final int taskId; // Unique identifier for this task.
String affinity; // The affinity name for this task, or null.
final IVoiceInteractionSession voiceSession; // Voice interaction session driving task
@@ -144,6 +148,10 @@
TaskRecord mNextAffiliate; // next task in affiliated chain.
int mNextAffiliateTaskId = -1; // next id for persistence.
+ // For relaunching the task from recents as though it was launched by the original launcher.
+ int mCallingUid;
+ String mCallingPackage;
+
final ActivityManagerService mService;
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
@@ -167,7 +175,7 @@
String _lastDescription, ArrayList<ActivityRecord> activities, long _firstActiveTime,
long _lastActiveTime, long lastTimeMoved, boolean neverRelinquishIdentity,
ActivityManager.TaskDescription _lastTaskDescription, int taskAffiliation,
- int prevTaskId, int nextTaskId) {
+ int prevTaskId, int nextTaskId, int callingUid, String callingPackage) {
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
@@ -196,17 +204,19 @@
mAffiliatedTaskId = taskAffiliation;
mPrevAffiliateTaskId = prevTaskId;
mNextAffiliateTaskId = nextTaskId;
+ mCallingUid = callingUid;
+ mCallingPackage = callingPackage;
}
void touchActiveTime() {
- lastActiveTime = android.os.SystemClock.elapsedRealtime();
+ lastActiveTime = System.currentTimeMillis();
if (firstActiveTime == 0) {
firstActiveTime = lastActiveTime;
}
}
long getInactiveDuration() {
- return android.os.SystemClock.elapsedRealtime() - lastActiveTime;
+ return System.currentTimeMillis() - lastActiveTime;
}
void setIntent(Intent _intent, ActivityInfo info) {
@@ -284,6 +294,9 @@
}
void setTaskToReturnTo(int taskToReturnTo) {
+ if (IGNORE_RETURN_TO_RECENTS && taskToReturnTo == RECENTS_ACTIVITY_TYPE) {
+ taskToReturnTo = HOME_ACTIVITY_TYPE;
+ }
mTaskToReturnTo = taskToReturnTo;
}
@@ -458,6 +471,8 @@
if (mActivities.isEmpty()) {
taskType = r.mActivityType;
isPersistable = r.isPersistable();
+ mCallingUid = r.launchedFromUid;
+ mCallingPackage = r.launchedFromPackage;
// Clamp to [1, 100].
maxRecents = Math.min(Math.max(r.info.maxRecents, 1), 100);
} else {
@@ -765,6 +780,8 @@
out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
+ out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
+ out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
if (affinityIntent != null) {
out.startTag(null, TAG_AFFINITYINTENT);
@@ -816,6 +833,8 @@
int taskAffiliation = -1;
int prevTaskId = -1;
int nextTaskId = -1;
+ int callingUid = -1;
+ String callingPackage = "";
for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
final String attrName = in.getAttributeName(attrNdx);
@@ -858,6 +877,10 @@
prevTaskId = Integer.valueOf(attrValue);
} else if (ATTR_NEXT_AFFILIATION.equals(attrName)) {
nextTaskId = Integer.valueOf(attrValue);
+ } else if (ATTR_CALLING_UID.equals(attrName)) {
+ callingUid = Integer.valueOf(attrValue);
+ } else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
+ callingPackage = attrValue;
} else {
Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
}
@@ -898,7 +921,8 @@
affinityIntent, affinity, realActivity, origActivity, rootHasReset,
autoRemoveRecents, askedCompatMode, taskType, userId, lastDescription, activities,
firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
- taskDescription, taskAffiliation, prevTaskId, nextTaskId);
+ taskDescription, taskAffiliation, prevTaskId, nextTaskId, callingUid,
+ callingPackage);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
activities.get(activityNdx).task = task;
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 15e3e89..1c26846 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -32,18 +32,23 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
+import android.os.IUserManager;
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Slog;
import android.util.SparseArray;
import java.util.HashSet;
+import java.util.List;
/**
* Implementation of the clipboard for copy and paste.
@@ -54,6 +59,7 @@
private final Context mContext;
private final IActivityManager mAm;
+ private final IUserManager mUm;
private final PackageManager mPm;
private final AppOpsManager mAppOps;
private final IBinder mPermissionOwner;
@@ -92,6 +98,7 @@
mContext = context;
mAm = ActivityManagerNative.getDefault();
mPm = context.getPackageManager();
+ mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
IBinder permOwner = null;
try {
@@ -161,32 +168,81 @@
return;
}
checkDataOwnerLocked(clip, callingUid);
- clearActiveOwnersLocked();
- PerUserClipboard clipboard = getClipboard();
- clipboard.primaryClip = clip;
- final long ident = Binder.clearCallingIdentity();
- final int n = clipboard.primaryClipListeners.beginBroadcast();
- try {
- for (int i = 0; i < n; i++) {
+ final int userId = UserHandle.getUserId(callingUid);
+ PerUserClipboard clipboard = getClipboard(userId);
+ revokeUris(clipboard);
+ setPrimaryClipInternal(clipboard, clip);
+ List<UserInfo> related = getRelatedProfiles(userId);
+ if (related != null) {
+ int size = related.size();
+ if (size > 1) { // Related profiles list include the current profile.
+ boolean canCopy = false;
try {
- ListenerInfo li = (ListenerInfo)
- clipboard.primaryClipListeners.getBroadcastCookie(i);
- if (mAppOps.checkOpNoThrow(AppOpsManager.OP_READ_CLIPBOARD, li.mUid,
- li.mPackageName) == AppOpsManager.MODE_ALLOWED) {
- clipboard.primaryClipListeners.getBroadcastItem(i)
- .dispatchPrimaryClipChanged();
- }
+ canCopy = !mUm.getUserRestrictions(userId).getBoolean(
+ UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
} catch (RemoteException e) {
- // The RemoteCallbackList will take care of removing
- // the dead object for us.
+ Slog.e(TAG, "Remote Exception calling UserManager: " + e);
+ }
+ // Copy clip data to related users if allowed. If disallowed, then remove
+ // primary clip in related users to prevent pasting stale content.
+ if (!canCopy) {
+ clip = null;
+ } else {
+ clip.fixUrisLight(userId);
+ }
+ for (int i = 0; i < size; i++) {
+ int id = related.get(i).id;
+ if (id != userId) {
+ setPrimaryClipInternal(getClipboard(id), clip);
+ }
}
}
- } finally {
- clipboard.primaryClipListeners.finishBroadcast();
- Binder.restoreCallingIdentity(ident);
}
}
}
+
+ List<UserInfo> getRelatedProfiles(int userId) {
+ final List<UserInfo> related;
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ related = mUm.getProfiles(userId, true);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote Exception calling UserManager: " + e);
+ return null;
+ } finally{
+ Binder.restoreCallingIdentity(origId);
+ }
+ return related;
+ }
+
+ void setPrimaryClipInternal(PerUserClipboard clipboard, ClipData clip) {
+ clipboard.activePermissionOwners.clear();
+ if (clip == null && clipboard.primaryClip == null) {
+ return;
+ }
+ clipboard.primaryClip = clip;
+ final long ident = Binder.clearCallingIdentity();
+ final int n = clipboard.primaryClipListeners.beginBroadcast();
+ try {
+ for (int i = 0; i < n; i++) {
+ try {
+ ListenerInfo li = (ListenerInfo)
+ clipboard.primaryClipListeners.getBroadcastCookie(i);
+ if (mAppOps.checkOpNoThrow(AppOpsManager.OP_READ_CLIPBOARD, li.mUid,
+ li.mPackageName) == AppOpsManager.MODE_ALLOWED) {
+ clipboard.primaryClipListeners.getBroadcastItem(i)
+ .dispatchPrimaryClipChanged();
+ }
+ } catch (RemoteException e) {
+ // The RemoteCallbackList will take care of removing
+ // the dead object for us.
+ }
+ }
+ } finally {
+ clipboard.primaryClipListeners.finishBroadcast();
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
public ClipData getPrimaryClip(String pkg) {
synchronized (this) {
@@ -257,7 +313,8 @@
try {
// This will throw SecurityException for us.
mAm.checkGrantUriPermission(uid, null, ContentProvider.getUriWithoutUserId(uri),
- Intent.FLAG_GRANT_READ_URI_PERMISSION, resolveUserId(uri, uid));
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(uid)));
} catch (RemoteException e) {
} finally {
Binder.restoreCallingIdentity(ident);
@@ -281,26 +338,26 @@
}
}
- private final void grantUriLocked(Uri uri, String pkg) {
+ private final void grantUriLocked(Uri uri, String pkg, int userId) {
long ident = Binder.clearCallingIdentity();
try {
+ int sourceUserId = ContentProvider.getUserIdFromUri(uri, userId);
+ uri = ContentProvider.getUriWithoutUserId(uri);
mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg,
- ContentProvider.getUriWithoutUserId(uri),
- Intent.FLAG_GRANT_READ_URI_PERMISSION,
- resolveUserId(uri, Process.myUid()));
+ uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, userId);
} catch (RemoteException e) {
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- private final void grantItemLocked(ClipData.Item item, String pkg) {
+ private final void grantItemLocked(ClipData.Item item, String pkg, int userId) {
if (item.getUri() != null) {
- grantUriLocked(item.getUri(), pkg);
+ grantUriLocked(item.getUri(), pkg, userId);
}
Intent intent = item.getIntent();
if (intent != null && intent.getData() != null) {
- grantUriLocked(intent.getData(), pkg);
+ grantUriLocked(intent.getData(), pkg, userId);
}
}
@@ -326,19 +383,21 @@
if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(pkg)) {
final int N = clipboard.primaryClip.getItemCount();
for (int i=0; i<N; i++) {
- grantItemLocked(clipboard.primaryClip.getItemAt(i), pkg);
+ grantItemLocked(clipboard.primaryClip.getItemAt(i), pkg, UserHandle.getUserId(uid));
}
clipboard.activePermissionOwners.add(pkg);
}
}
private final void revokeUriLocked(Uri uri) {
+ int userId = ContentProvider.getUserIdFromUri(uri,
+ UserHandle.getUserId(Binder.getCallingUid()));
long ident = Binder.clearCallingIdentity();
try {
- mAm.revokeUriPermissionFromOwner(mPermissionOwner,
- ContentProvider.getUriWithoutUserId(uri),
+ uri = ContentProvider.getUriWithoutUserId(uri);
+ mAm.revokeUriPermissionFromOwner(mPermissionOwner, uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- resolveUserId(uri, Process.myUid()));
+ userId);
} catch (RemoteException e) {
} finally {
Binder.restoreCallingIdentity(ident);
@@ -355,9 +414,7 @@
}
}
- private final void clearActiveOwnersLocked() {
- PerUserClipboard clipboard = getClipboard();
- clipboard.activePermissionOwners.clear();
+ private final void revokeUris(PerUserClipboard clipboard) {
if (clipboard.primaryClip == null) {
return;
}
@@ -366,8 +423,4 @@
revokeItemLocked(clipboard.primaryClip.getItemAt(i));
}
}
-
- private final int resolveUserId(Uri uri, int uid) {
- return ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(uid));
- }
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 01a2fc2..fa8626f 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -27,7 +27,6 @@
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
-import android.net.IConnectivityManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
@@ -95,7 +94,6 @@
private final INetworkManagementService mNMService;
private final INetworkStatsService mStatsService;
- private final IConnectivityManager mConnService;
private Looper mLooper;
private HashMap<String, TetherInterfaceSM> mIfaces; // all tethered/tetherable ifaces
@@ -132,11 +130,10 @@
// when RNDIS is enabled
public Tethering(Context context, INetworkManagementService nmService,
- INetworkStatsService statsService, IConnectivityManager connService, Looper looper) {
+ INetworkStatsService statsService, Looper looper) {
mContext = context;
mNMService = nmService;
mStatsService = statsService;
- mConnService = connService;
mLooper = looper;
mPublicSync = new Object();
@@ -176,6 +173,12 @@
mDefaultDnsServers[1] = DNS_DEFAULT_SERVER2;
}
+ // We can't do this once in the Tethering() constructor and cache the value, because the
+ // CONNECTIVITY_SERVICE is registered only after the Tethering() constructor has completed.
+ private ConnectivityManager getConnectivityManager() {
+ return (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
void updateConfiguration() {
String[] tetherableUsbRegexs = mContext.getResources().getStringArray(
com.android.internal.R.array.config_tether_usb_regexs);
@@ -367,11 +370,7 @@
// TODO - move all private methods used only by the state machine into the state machine
// to clarify what needs synchronized protection.
private void sendTetherStateChangedBroadcast() {
- try {
- if (!mConnService.isTetheringSupported()) return;
- } catch (RemoteException e) {
- return;
- }
+ if (!getConnectivityManager().isTetheringSupported()) return;
ArrayList<String> availableList = new ArrayList<String>();
ArrayList<String> activeList = new ArrayList<String>();
@@ -724,14 +723,6 @@
return retVal;
}
- //TODO: Temporary handling upstream change triggered without
- // CONNECTIVITY_ACTION. Only to accomodate interface
- // switch during HO.
- // @see bug/4455071
- public void handleTetherIfaceChange() {
- mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
- }
-
class TetherInterfaceSM extends StateMachine {
// notification from the master SM that it's not in tether mode
static final int CMD_TETHER_MODE_DEAD = 1;
@@ -1196,11 +1187,8 @@
int result = PhoneConstants.APN_REQUEST_FAILED;
String enableString = enableString(apnType);
if (enableString == null) return false;
- try {
- result = mConnService.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
- enableString, new Binder());
- } catch (Exception e) {
- }
+ result = getConnectivityManager().startUsingNetworkFeature(
+ ConnectivityManager.TYPE_MOBILE, enableString);
switch (result) {
case PhoneConstants.APN_ALREADY_ACTIVE:
case PhoneConstants.APN_REQUEST_STARTED:
@@ -1221,12 +1209,8 @@
// ignore pending renewal requests
++mCurrentConnectionSequence;
if (mMobileApnReserved != ConnectivityManager.TYPE_NONE) {
- try {
- mConnService.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
- enableString(mMobileApnReserved));
- } catch (Exception e) {
- return false;
- }
+ getConnectivityManager().stopUsingNetworkFeature(
+ ConnectivityManager.TYPE_MOBILE, enableString(mMobileApnReserved));
mMobileApnReserved = ConnectivityManager.TYPE_NONE;
}
return true;
@@ -1289,10 +1273,8 @@
}
for (Integer netType : mUpstreamIfaceTypes) {
- NetworkInfo info = null;
- try {
- info = mConnService.getNetworkInfo(netType.intValue());
- } catch (RemoteException e) { }
+ NetworkInfo info =
+ getConnectivityManager().getNetworkInfo(netType.intValue());
if ((info != null) && info.isConnected()) {
upType = netType.intValue();
break;
@@ -1330,10 +1312,8 @@
sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
}
} else {
- LinkProperties linkProperties = null;
- try {
- linkProperties = mConnService.getLinkPropertiesForType(upType);
- } catch (RemoteException e) { }
+ LinkProperties linkProperties =
+ getConnectivityManager().getLinkProperties(upType);
if (linkProperties != null) {
// Find the interface with the default IPv4 route. It may be the
// interface described by linkProperties, or one of the interfaces
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index d15254b..4b5a2cb 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -77,6 +77,7 @@
import com.android.server.net.BaseNetworkObserver;
import java.io.File;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
@@ -470,6 +471,13 @@
if (oldInterface != null && !oldInterface.equals(interfaze)) {
jniReset(oldInterface);
}
+
+ try {
+ IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking);
+ } catch (IOException e) {
+ throw new IllegalStateException(
+ "Cannot set tunnel's fd as blocking=" + config.blocking, e);
+ }
} catch (RuntimeException e) {
IoUtils.closeQuietly(tun);
agentDisconnect();
@@ -488,18 +496,6 @@
return tun;
}
- /**
- * Check if a given address is covered by the VPN's routing rules.
- */
- public boolean isAddressCovered(InetAddress address) {
- synchronized (Vpn.this) {
- if (!isRunningLocked()) {
- return false;
- }
- return RouteInfo.selectBestRoute(mConfig.routes, address) != null;
- }
- }
-
private boolean isRunningLocked() {
return mVpnUsers != null;
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 1279c6e..64d3dc5 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -719,10 +719,16 @@
@Override
public void setMasterSyncAutomatically(boolean flag) {
+ setMasterSyncAutomaticallyAsUser(flag, UserHandle.getCallingUserId());
+ }
+
+ @Override
+ public void setMasterSyncAutomaticallyAsUser(boolean flag, int userId) {
+ enforceCrossUserPermission(userId,
+ "no permission to set the sync status for user " + userId);
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
- int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 4740cae..45d3771 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -26,7 +26,6 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -199,9 +198,10 @@
return mScreenAutoBrightness;
}
- public void updatePowerState(DisplayManagerInternal.DisplayPowerRequest request) {
- if (setScreenAutoBrightnessAdjustment(request.screenAutoBrightnessAdjustment)
- | setLightSensorEnabled(request.wantLightSensorEnabled())) {
+ public void configure(boolean enable, float adjustment) {
+ boolean changed = setLightSensorEnabled(enable);
+ changed |= setScreenAutoBrightnessAdjustment(adjustment);
+ if (changed) {
updateAutoBrightness(false /*sendUpdate*/);
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index a361e10..09221a3e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -213,9 +213,11 @@
// The elapsed real time when the screen on was blocked.
private long mScreenOnBlockStartRealTime;
- // True if the screen auto-brightness value is actually being used to
- // set the display brightness.
- private boolean mUsingScreenAutoBrightness;
+ // Remembers whether certain kinds of brightness adjustments
+ // were recently applied so that we can decide how to transition.
+ private boolean mAppliedAutoBrightness;
+ private boolean mAppliedDimming;
+ private boolean mAppliedLowPower;
// The controller for the automatic brightness level.
private AutomaticBrightnessController mAutomaticBrightnessController;
@@ -428,7 +430,6 @@
// Update the power state request.
final boolean mustNotify;
boolean mustInitialize = false;
- boolean wasDimOrDoze = false;
boolean autoBrightnessAdjustmentChanged = false;
synchronized (mLock) {
@@ -444,8 +445,6 @@
mPendingRequestChangedLocked = false;
mustInitialize = true;
} else if (mPendingRequestChangedLocked) {
- wasDimOrDoze = (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM
- || mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DOZE);
autoBrightnessAdjustmentChanged = (mPowerRequest.screenAutoBrightnessAdjustment
!= mPendingRequestLocked.screenAutoBrightnessAdjustment);
mPowerRequest.copyFrom(mPendingRequestLocked);
@@ -463,10 +462,32 @@
initialize();
}
+ // Compute the basic display state using the policy.
+ // We might override this below based on other factors.
+ int state;
+ int brightness = PowerManager.BRIGHTNESS_DEFAULT;
+ switch (mPowerRequest.policy) {
+ case DisplayPowerRequest.POLICY_OFF:
+ state = Display.STATE_OFF;
+ break;
+ case DisplayPowerRequest.POLICY_DOZE:
+ if (mPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) {
+ state = mPowerRequest.dozeScreenState;
+ } else {
+ state = Display.STATE_DOZE;
+ }
+ brightness = mPowerRequest.dozeScreenBrightness;
+ break;
+ case DisplayPowerRequest.POLICY_DIM:
+ case DisplayPowerRequest.POLICY_BRIGHT:
+ default:
+ state = Display.STATE_ON;
+ break;
+ }
+
// Apply the proximity sensor.
if (mProximitySensor != null) {
- if (mPowerRequest.useProximitySensor
- && mPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) {
+ if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
setProximitySensorEnabled(true);
if (!mScreenOffBecauseOfProximity
&& mProximity == PROXIMITY_POSITIVE) {
@@ -476,7 +497,7 @@
} else if (mWaitingForNegativeProximity
&& mScreenOffBecauseOfProximity
&& mProximity == PROXIMITY_POSITIVE
- && mPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) {
+ && state != Display.STATE_OFF) {
setProximitySensorEnabled(true);
} else {
setProximitySensorEnabled(false);
@@ -490,63 +511,89 @@
} else {
mWaitingForNegativeProximity = false;
}
-
- // Turn on the light sensor if needed.
- if (mAutomaticBrightnessController != null) {
- mAutomaticBrightnessController.updatePowerState(mPowerRequest);
+ if (mScreenOffBecauseOfProximity) {
+ state = Display.STATE_OFF;
}
- // Set the screen brightness.
- if (mPowerRequest.wantScreenOnAny()) {
- int target;
- boolean slow;
- int screenAutoBrightness = mAutomaticBrightnessController != null ?
- mAutomaticBrightnessController.getAutomaticScreenBrightness() : -1;
- if (screenAutoBrightness >= 0 && mPowerRequest.useAutoBrightness) {
- // Use current auto-brightness value.
- target = screenAutoBrightness;
- slow = mUsingScreenAutoBrightness && !autoBrightnessAdjustmentChanged;
- mUsingScreenAutoBrightness = true;
+ // Use zero brightness when screen is off.
+ if (state == Display.STATE_OFF) {
+ brightness = PowerManager.BRIGHTNESS_OFF;
+ }
+
+ // Use default brightness when dozing unless overridden.
+ if (brightness < 0 && (state == Display.STATE_DOZE
+ || state == Display.STATE_DOZE_SUSPEND)) {
+ brightness = mScreenBrightnessDozeConfig;
+ }
+
+ // Configure auto-brightness.
+ boolean autoBrightnessEnabled = false;
+ if (mAutomaticBrightnessController != null) {
+ autoBrightnessEnabled = mPowerRequest.useAutoBrightness
+ && state == Display.STATE_ON && brightness < 0;
+ mAutomaticBrightnessController.configure(autoBrightnessEnabled,
+ mPowerRequest.screenAutoBrightnessAdjustment);
+ }
+
+ // Apply auto-brightness.
+ boolean slowChange = false;
+ if (brightness < 0) {
+ if (autoBrightnessEnabled) {
+ brightness = mAutomaticBrightnessController.getAutomaticScreenBrightness();
+ }
+ if (brightness >= 0) {
+ // Use current auto-brightness value and slowly adjust to changes.
+ brightness = clampScreenBrightness(brightness);
+ if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
+ slowChange = true; // slowly adapt to auto-brightness
+ }
+ mAppliedAutoBrightness = true;
} else {
- // Light sensor is disabled or not ready yet.
- // Use the current brightness setting from the request, which is expected
- // provide a nominal default value for the case where auto-brightness
- // is not ready yet.
- target = mPowerRequest.screenBrightness;
- slow = false;
- mUsingScreenAutoBrightness = false;
+ mAppliedAutoBrightness = false;
}
- if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DOZE) {
- // Dim quickly to the doze state.
- target = mScreenBrightnessDozeConfig;
- slow = false;
- } else if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) {
- // Dim quickly by at least some minimum amount.
- target = Math.min(target - SCREEN_DIM_MINIMUM_REDUCTION,
- mScreenBrightnessDimConfig);
- slow = false;
- } else if (wasDimOrDoze) {
- // Brighten quickly.
- slow = false;
- }
- // If low power mode is enabled, brightness level
- // would be scaled down to half
- if (mPowerRequest.lowPowerMode) {
- target = target/2;
- }
- animateScreenBrightness(clampScreenBrightness(target),
- slow ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
} else {
- // Screen is off. Don't bother changing the brightness.
- mUsingScreenAutoBrightness = false;
+ mAppliedAutoBrightness = false;
+ }
+
+ // Apply manual brightness.
+ // Use the current brightness setting from the request, which is expected
+ // provide a nominal default value for the case where auto-brightness
+ // is not ready yet.
+ if (brightness < 0) {
+ brightness = clampScreenBrightness(mPowerRequest.screenBrightness);
+ }
+
+ // Apply dimming by at least some minimum amount when user activity
+ // timeout is about to expire.
+ if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
+ brightness = Math.max(Math.min(brightness - SCREEN_DIM_MINIMUM_REDUCTION,
+ mScreenBrightnessDimConfig), mScreenBrightnessRangeMinimum);
+ if (!mAppliedDimming) {
+ slowChange = false;
+ }
+ mAppliedDimming = true;
+ }
+
+ // If low power mode is enabled, cut the brightness level by half
+ // as long as it is above the minimum threshold.
+ if (mPowerRequest.lowPowerMode) {
+ if (brightness > mScreenBrightnessRangeMinimum) {
+ brightness = Math.max(brightness / 2, mScreenBrightnessRangeMinimum);
+ }
+ if (!mAppliedLowPower) {
+ slowChange = false;
+ }
+ mAppliedLowPower = true;
+ }
+
+ // Animate the screen brightness when the screen is on.
+ if (state != Display.STATE_OFF) {
+ animateScreenBrightness(brightness, slowChange
+ ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
}
// Animate the screen on or off unless blocked.
- if (mScreenOffBecauseOfProximity) {
- // Screen off due to proximity.
- setScreenState(Display.STATE_OFF);
- unblockScreenOn();
- } else if (mPowerRequest.wantScreenOnAny()) {
+ if (state == Display.STATE_ON) {
// Want screen on.
// Wait for previous off animation to complete beforehand.
// It is relatively short but if we cancel it and switch to the
@@ -555,21 +602,14 @@
// Turn the screen on. The contents of the screen may not yet
// be visible if the electron beam has not been dismissed because
// its last frame of animation is solid black.
-
- if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DOZE) {
- if (!mScreenBrightnessRampAnimator.isAnimating()) {
- setScreenState(Display.STATE_DOZE);
- }
- } else {
- setScreenState(Display.STATE_ON);
- }
-
+ setScreenState(Display.STATE_ON);
if (mPowerRequest.blockScreenOn
&& mPowerState.getElectronBeamLevel() == 0.0f) {
blockScreenOn();
} else {
unblockScreenOn();
- if (USE_ELECTRON_BEAM_ON_ANIMATION) {
+ if (USE_ELECTRON_BEAM_ON_ANIMATION && mPowerRequest.isBrightOrDim()) {
+ // Perform screen on animation.
if (!mElectronBeamOnAnimator.isStarted()) {
if (mPowerState.getElectronBeamLevel() == 1.0f) {
mPowerState.dismissElectronBeam();
@@ -583,28 +623,59 @@
}
}
} else {
+ // Skip screen on animation.
mPowerState.setElectronBeamLevel(1.0f);
mPowerState.dismissElectronBeam();
}
}
}
+ } else if (state == Display.STATE_DOZE) {
+ // Want screen dozing.
+ // Wait for brightness animation to complete beforehand when entering doze
+ // from screen on.
+ unblockScreenOn();
+ if (!mScreenBrightnessRampAnimator.isAnimating()
+ || mPowerState.getScreenState() != Display.STATE_ON) {
+ // Set screen state and dismiss the black surface without fanfare.
+ setScreenState(state);
+ mPowerState.setElectronBeamLevel(1.0f);
+ mPowerState.dismissElectronBeam();
+ }
+ } else if (state == Display.STATE_DOZE_SUSPEND) {
+ // Want screen dozing and suspended.
+ // Wait for brightness animation to complete beforehand unless already
+ // suspended because we may not be able to change it after suspension.
+ unblockScreenOn();
+ if (!mScreenBrightnessRampAnimator.isAnimating()
+ || mPowerState.getScreenState() == Display.STATE_DOZE_SUSPEND) {
+ // Set screen state and dismiss the black surface without fanfare.
+ setScreenState(state);
+ mPowerState.setElectronBeamLevel(1.0f);
+ mPowerState.dismissElectronBeam();
+ }
} else {
// Want screen off.
// Wait for previous on animation to complete beforehand.
unblockScreenOn();
if (!mElectronBeamOnAnimator.isStarted()) {
- if (!mElectronBeamOffAnimator.isStarted()) {
- if (mPowerState.getElectronBeamLevel() == 0.0f) {
- setScreenState(Display.STATE_OFF);
- } else if (mPowerState.prepareElectronBeam(
- mElectronBeamFadesConfig ?
- ElectronBeam.MODE_FADE :
- ElectronBeam.MODE_COOL_DOWN)
- && mPowerState.getScreenState() != Display.STATE_OFF) {
- mElectronBeamOffAnimator.start();
- } else {
- mElectronBeamOffAnimator.end();
+ if (mPowerRequest.policy == DisplayPowerRequest.POLICY_OFF) {
+ // Perform screen off animation.
+ if (!mElectronBeamOffAnimator.isStarted()) {
+ if (mPowerState.getElectronBeamLevel() == 0.0f) {
+ setScreenState(Display.STATE_OFF);
+ } else if (mPowerState.prepareElectronBeam(
+ mElectronBeamFadesConfig ?
+ ElectronBeam.MODE_FADE :
+ ElectronBeam.MODE_COOL_DOWN)
+ && mPowerState.getScreenState() != Display.STATE_OFF) {
+ mElectronBeamOffAnimator.start();
+ } else {
+ mElectronBeamOffAnimator.end();
+ }
}
+ } else {
+ // Skip screen off animation.
+ setScreenState(Display.STATE_OFF);
}
}
}
@@ -856,7 +927,9 @@
pw.println(" mPendingProximityDebounceTime="
+ TimeUtils.formatUptime(mPendingProximityDebounceTime));
pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
- pw.println(" mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness);
+ pw.println(" mAppliedAutoBrightness=" + mAppliedAutoBrightness);
+ pw.println(" mAppliedDimming=" + mAppliedDimming);
+ pw.println(" mAppliedLowPower=" + mAppliedLowPower);
pw.println(" mScreenBrightnessRampAnimator.isAnimating()=" +
mScreenBrightnessRampAnimator.isAnimating());
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index a5f8849..4821e74 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -402,13 +402,14 @@
Slog.d(TAG, "Updating screen state: state="
+ Display.stateToString(state) + ", backlight=" + backlight);
}
- if (stateChanged && state != Display.STATE_OFF) {
+ boolean suspending = Display.isSuspendedState(state);
+ if (stateChanged && !suspending) {
mBlanker.requestDisplayState(state);
}
if (backlightChanged) {
mBacklight.setBrightness(backlight);
}
- if (stateChanged && state == Display.STATE_OFF) {
+ if (stateChanged && suspending) {
mBlanker.requestDisplayState(state);
}
}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 4ccf73b..107a6f6 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -38,6 +38,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
+import android.os.PowerManagerInternal;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -48,6 +49,7 @@
import android.service.dreams.IDreamManager;
import android.text.TextUtils;
import android.util.Slog;
+import android.view.Display;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -71,6 +73,7 @@
private final DreamHandler mHandler;
private final DreamController mController;
private final PowerManager mPowerManager;
+ private final PowerManagerInternal mPowerManagerInternal;
private final PowerManager.WakeLock mDozeWakeLock;
private final McuHal mMcuHal; // synchronized on self
@@ -81,6 +84,8 @@
private boolean mCurrentDreamCanDoze;
private boolean mCurrentDreamIsDozing;
private boolean mCurrentDreamIsWaking;
+ private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
+ private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
private DozeHardwareWrapper mCurrentDreamDozeHardware;
public DreamManagerService(Context context) {
@@ -90,6 +95,7 @@
mController = new DreamController(context, mHandler, mControllerListener);
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mPowerManagerInternal = getLocalService(PowerManagerInternal.class);
mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG);
mMcuHal = McuHal.open();
@@ -134,6 +140,9 @@
pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze);
pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing);
pw.println("mCurrentDreamIsWaking=" + mCurrentDreamIsWaking);
+ pw.println("mCurrentDreamDozeScreenState="
+ + Display.stateToString(mCurrentDreamDozeScreenState));
+ pw.println("mCurrentDreamDozeScreenBrightness=" + mCurrentDreamDozeScreenBrightness);
pw.println("mCurrentDreamDozeHardware=" + mCurrentDreamDozeHardware);
pw.println("getDozeComponent()=" + getDozeComponent());
pw.println();
@@ -213,16 +222,24 @@
}
}
- private void startDozingInternal(IBinder token) {
+ private void startDozingInternal(IBinder token, int screenState,
+ int screenBrightness) {
if (DEBUG) {
- Slog.d(TAG, "Dream requested to start dozing: " + token);
+ Slog.d(TAG, "Dream requested to start dozing: " + token
+ + ", screenState=" + screenState
+ + ", screenBrightness=" + screenBrightness);
}
synchronized (mLock) {
- if (mCurrentDreamToken == token && mCurrentDreamCanDoze
- && !mCurrentDreamIsDozing) {
- mCurrentDreamIsDozing = true;
- mDozeWakeLock.acquire();
+ if (mCurrentDreamToken == token && mCurrentDreamCanDoze) {
+ mCurrentDreamDozeScreenState = screenState;
+ mCurrentDreamDozeScreenBrightness = screenBrightness;
+ mPowerManagerInternal.setDozeOverrideFromDreamManager(
+ screenState, screenBrightness);
+ if (!mCurrentDreamIsDozing) {
+ mCurrentDreamIsDozing = true;
+ mDozeWakeLock.acquire();
+ }
}
}
}
@@ -236,6 +253,8 @@
if (mCurrentDreamToken == token && mCurrentDreamIsDozing) {
mCurrentDreamIsDozing = false;
mDozeWakeLock.release();
+ mPowerManagerInternal.setDozeOverrideFromDreamManager(
+ Display.STATE_UNKNOWN, PowerManager.BRIGHTNESS_DEFAULT);
}
}
}
@@ -399,6 +418,8 @@
mCurrentDreamIsDozing = false;
mDozeWakeLock.release();
}
+ mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
+ mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
if (mCurrentDreamDozeHardware != null) {
mCurrentDreamDozeHardware.release();
mCurrentDreamDozeHardware = null;
@@ -593,7 +614,7 @@
}
@Override // Binder call
- public void startDozing(IBinder token) {
+ public void startDozing(IBinder token, int screenState, int screenBrightness) {
// Requires no permission, called by Dream from an arbitrary process.
if (token == null) {
throw new IllegalArgumentException("token must not be null");
@@ -601,7 +622,7 @@
final long ident = Binder.clearCallingIdentity();
try {
- startDozingInternal(token);
+ startDozingInternal(token, screenState, screenBrightness);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 384cd4a..946d4ce 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -16,6 +16,8 @@
package com.android.server.hdmi;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+
/**
* Defines constants related to HDMI-CEC protocol internal implementation.
* If a constant will be used in the public api, it should be located in
@@ -78,7 +80,7 @@
public static final int ADDR_INVALID = -1;
/** Logical address used to indicate the source comes from internal device. */
- public static final int ADDR_INTERNAL = 0xFFFF;
+ public static final int ADDR_INTERNAL = HdmiCecDeviceInfo.ADDR_INTERNAL;
static final int MESSAGE_FEATURE_ABORT = 0x00;
static final int MESSAGE_IMAGE_VIEW_ON = 0x04;
@@ -179,10 +181,11 @@
static final int INVALID_PORT_ID = -1;
static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
- // Send result codes.
+ // Send result codes. It should be consistent with hdmi_cec.h's send_message error code.
static final int SEND_RESULT_SUCCESS = 0;
- static final int SEND_RESULT_NAK = -1;
- static final int SEND_RESULT_FAILURE = -2;
+ static final int SEND_RESULT_NAK = 1;
+ static final int SEND_RESULT_BUSY = 2;
+ static final int SEND_RESULT_FAILURE = 3;
// Strategy for device polling.
// Should use "OR(|) operation of POLL_STRATEGY_XXX and POLL_ITERATION_XXX.
@@ -206,5 +209,10 @@
static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "hdmi_cec.prefaddr.playback";
static final String PROPERTY_PREFERRED_ADDRESS_TV = "hdmi_cec.prefaddr.tv";
+ static final int RECORDING_TYPE_DIGITAL_RF = 1;
+ static final int RECORDING_TYPE_ANALOGUE_RF = 2;
+ static final int RECORDING_TYPE_EXTERNAL_PHYSICAL_ADDRESS = 3;
+ static final int RECORDING_TYPE_OWN_SOURCE = 4;
+
private Constants() { /* cannot be instantiated */ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 72519f2..cbccc1d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -506,15 +506,20 @@
&& mService.handleCecCommand(message)) {
return;
}
-
- if (message.getDestination() != Constants.ADDR_BROADCAST) {
- int sourceAddress = message.getDestination();
- // Reply <Feature Abort> to initiator (source) for all requests.
- HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
- sourceAddress, message.getSource(), message.getOpcode(),
- Constants.ABORT_REFUSED);
- sendCommand(cecMessage);
+ if (message.getDestination() == Constants.ADDR_BROADCAST) {
+ return;
}
+ if (message.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) {
+ Slog.v(TAG, "Unhandled <Feature Abort> message:" + message);
+ return;
+ }
+
+ int sourceAddress = message.getDestination();
+ // Reply <Feature Abort> to initiator (source) for all requests.
+ HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ sourceAddress, message.getSource(), message.getOpcode(),
+ Constants.ABORT_REFUSED);
+ sendCommand(cecMessage);
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index f1e7ff2..dbcdfc0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -200,6 +200,8 @@
return handleVendorCommandWithId(message);
case Constants.MESSAGE_SET_OSD_NAME:
return handleSetOsdName(message);
+ case Constants.MESSAGE_RECORD_TV_SCREEN:
+ return handleRecordTvScreen(message);
default:
return false;
}
@@ -403,6 +405,13 @@
return true;
}
+ protected boolean handleRecordTvScreen(HdmiCecMessage message) {
+ // The default behavior of <Record TV Screen> is replying <Feature Abort> with "Refused".
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
+ message.getSource(), message.getOpcode(), Constants.ABORT_REFUSED));
+ return true;
+ }
+
@ServiceThreadOnly
final void handleAddressAllocated(int logicalAddress, boolean fromBootup) {
assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 6aa86a7..bbecafa 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -19,6 +19,7 @@
import android.content.Intent;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiRecordSources;
import android.hardware.hdmi.IHdmiControlCallback;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -35,6 +36,7 @@
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -135,7 +137,10 @@
void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
assertRunOnServiceThread();
if (targetAddress == Constants.ADDR_INTERNAL) {
- handleSelectInternalSource(callback);
+ handleSelectInternalSource();
+ // Switching to internal source is always successful even when CEC control is disabled.
+ setActiveSource(targetAddress);
+ invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
return;
}
if (!mService.isControlEnabled()) {
@@ -153,7 +158,7 @@
}
@ServiceThreadOnly
- private void handleSelectInternalSource(IHdmiControlCallback callback) {
+ private void handleSelectInternalSource() {
assertRunOnServiceThread();
// Seq #18
if (mService.isControlEnabled() && getActiveSource() != mAddress) {
@@ -553,6 +558,9 @@
@ServiceThreadOnly
private void clearDeviceInfoList() {
assertRunOnServiceThread();
+ for (HdmiCecDeviceInfo info : mSafeExternalInputs) {
+ mService.invokeDeviceEventListeners(info, false);
+ }
mDeviceInfos.clear();
updateSafeDeviceInfoList();
}
@@ -802,6 +810,26 @@
return true;
}
+ // Seq #53
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleRecordTvScreen(HdmiCecMessage message) {
+ List<OneTouchRecordAction> actions = getActions(OneTouchRecordAction.class);
+ if (!actions.isEmpty()) {
+ // Assumes only one OneTouchRecordAction.
+ OneTouchRecordAction action = actions.get(0);
+ if (action.getRecorderAddress() != message.getSource()) {
+ displayOsd(HdmiControlManager.MESSAGE_NO_RECORDING_PREVIOUS_RECORDING_IN_PROGRESS);
+ }
+ return super.handleRecordTvScreen(message);
+ }
+
+ int recorderAddress = message.getSource();
+ byte[] recordSource = mService.invokeRecordRequestListener(recorderAddress);
+ startOneTouchRecord(recorderAddress, recordSource);
+ return true;
+ }
+
private boolean isMessageForSystemAudio(HdmiCecMessage message) {
if (message.getSource() != Constants.ADDR_AUDIO_SYSTEM
|| message.getDestination() != Constants.ADDR_TV
@@ -1123,9 +1151,12 @@
// LocalDeviceTv.onAddressAllocated() -> launchDeviceDiscovery().
removeAction(DeviceDiscoveryAction.class);
removeAction(HotplugDetectionAction.class);
+ // Remove one touch record action.
+ removeAction(OneTouchRecordAction.class);
disableSystemAudioIfExist();
disableArcIfExist();
+ clearDeviceInfoList();
checkIfPendingActionsCleared();
}
@@ -1203,4 +1234,72 @@
mService.getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
HdmiControlService.PERMISSION);
}
+
+ // Seq #54 and #55
+ @ServiceThreadOnly
+ void startOneTouchRecord(int recorderAddress, byte[] recordSource) {
+ assertRunOnServiceThread();
+ if (!mService.isControlEnabled()) {
+ Slog.w(TAG, "Can not start one touch record. CEC control is disabled.");
+ return;
+ }
+
+ if (!checkRecorder(recorderAddress)) {
+ Slog.w(TAG, "Invalid recorder address:" + recorderAddress);
+ return;
+ }
+
+ if (!checkRecordSource(recordSource)) {
+ Slog.w(TAG, "Invalid record source." + Arrays.toString(recordSource));
+ return;
+ }
+
+ addAndStartAction(new OneTouchRecordAction(this, recorderAddress, recordSource));
+ Slog.i(TAG, "Start new [One Touch Record]-Target:" + recorderAddress + ", recordSource:"
+ + Arrays.toString(recordSource));
+ }
+
+ @ServiceThreadOnly
+ void stopOneTouchRecord(int recorderAddress) {
+ assertRunOnServiceThread();
+ if (!mService.isControlEnabled()) {
+ Slog.w(TAG, "Can not stop one touch record. CEC control is disabled.");
+ return;
+ }
+
+ if (!checkRecorder(recorderAddress)) {
+ Slog.w(TAG, "Invalid recorder address:" + recorderAddress);
+ return;
+ }
+
+ // Remove one touch record action so that other one touch record can be started.
+ removeAction(OneTouchRecordAction.class);
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildRecordOff(mAddress, recorderAddress));
+ Slog.i(TAG, "Stop [One Touch Record]-Target:" + recorderAddress);
+ }
+
+ private boolean checkRecorder(int recorderAddress) {
+ HdmiCecDeviceInfo device = getDeviceInfo(recorderAddress);
+ return (device != null)
+ && (HdmiUtils.getTypeFromAddress(recorderAddress)
+ == HdmiCecDeviceInfo.DEVICE_RECORDER);
+ }
+
+ private boolean checkRecordSource(byte[] recordSource) {
+ return (recordSource != null) && HdmiRecordSources.checkRecordSource(recordSource);
+ }
+
+ @ServiceThreadOnly
+ void startTimerRecording(int recorderAddress, int sourceType, byte[] recordSource) {
+ assertRunOnServiceThread();
+
+ // TODO: implement this.
+ }
+
+ @ServiceThreadOnly
+ void clearTimerRecording(int recorderAddress, int sourceType, byte[] recordSource) {
+ assertRunOnServiceThread();
+
+ // TODO: implement this.
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 0b6c3c5..79f1964 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -455,6 +455,29 @@
return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, params);
}
+ /**
+ * Build <Record On> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param params parameter of command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildRecordOn(int src, int dest, byte[] params) {
+ return buildCommand(src, dest, Constants.MESSAGE_RECORD_ON, params);
+ }
+
+ /**
+ * Build <Record Off> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildRecordOff(int src, int dest) {
+ return buildCommand(src, dest, Constants.MESSAGE_RECORD_OFF);
+ }
+
/***** Please ADD new buildXXX() methods above. ******/
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 0239c83..7672232 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -32,6 +32,7 @@
import android.hardware.hdmi.IHdmiDeviceEventListener;
import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.hardware.hdmi.IHdmiInputChangeListener;
+import android.hardware.hdmi.IHdmiRecordRequestListener;
import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
import android.hardware.hdmi.IHdmiVendorCommandListener;
import android.media.AudioManager;
@@ -54,6 +55,8 @@
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
import com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
+import libcore.util.EmptyArray;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -156,6 +159,12 @@
@GuardedBy("mLock")
private InputChangeListenerRecord mInputChangeListenerRecord;
+ @GuardedBy("mLock")
+ private IHdmiRecordRequestListener mRecordRequestListener;
+
+ @GuardedBy("mLock")
+ private HdmiRecordRequestListenerRecord mRecordRequestListenerRecord;
+
// Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
// handling will be disabled and no request will be handled.
@GuardedBy("mLock")
@@ -683,6 +692,15 @@
}
}
+ private class HdmiRecordRequestListenerRecord implements IBinder.DeathRecipient {
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ mRecordRequestListener = null;
+ }
+ }
+ }
+
private void enforceAccessPermission() {
getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
}
@@ -1010,6 +1028,69 @@
}
});
}
+
+ @Override
+ public void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener) {
+ HdmiControlService.this.setOneTouchRecordRequestListener(listener);
+ }
+
+ @Override
+ public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ if (!isTvDevice()) {
+ Slog.w(TAG, "No TV is available.");
+ return;
+ }
+ tv().startOneTouchRecord(recorderAddress, recordSource);
+ }
+ });
+ }
+
+ @Override
+ public void stopOneTouchRecord(final int recorderAddress) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ if (!isTvDevice()) {
+ Slog.w(TAG, "No TV is available.");
+ return;
+ }
+ tv().stopOneTouchRecord(recorderAddress);
+ }
+ });
+ }
+
+ @Override
+ public void startTimerRecording(final int recorderAddress, final int sourceType,
+ final byte[] recordSource) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ if (!isTvDevice()) {
+ Slog.w(TAG, "No TV is available.");
+ return;
+ }
+ tv().startTimerRecording(recorderAddress, sourceType, recordSource);
+ }
+ });
+ }
+
+ @Override
+ public void clearTimerRecording(final int recorderAddress, final int sourceType,
+ final byte[] recordSource) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ if (!isTvDevice()) {
+ Slog.w(TAG, "No TV is available.");
+ return;
+ }
+ tv().clearTimerRecording(recorderAddress, sourceType, recordSource);
+ }
+ });
+ }
}
@ServiceThreadOnly
@@ -1153,6 +1234,32 @@
}
}
+ private void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener) {
+ synchronized (mLock) {
+ mRecordRequestListenerRecord = new HdmiRecordRequestListenerRecord();
+ try {
+ listener.asBinder().linkToDeath(mRecordRequestListenerRecord, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Listener already died", e);
+ return;
+ }
+ mRecordRequestListener = listener;
+ }
+ }
+
+ byte[] invokeRecordRequestListener(int recorderAddress) {
+ synchronized (mLock) {
+ try {
+ if (mRecordRequestListener != null) {
+ return mRecordRequestListener.onRecordRequestReceived(recorderAddress);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to start record.", e);
+ }
+ return EmptyArray.BYTE;
+ }
+ }
+
private void invokeCallback(IHdmiControlCallback callback, int result) {
try {
callback.onComplete(result);
diff --git a/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java b/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java
index 7646027..9ecbc5e 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java
@@ -16,30 +16,118 @@
package com.android.server.hdmi;
+import static android.hardware.hdmi.HdmiControlManager.MESSAGE_NO_RECORDING_CHECK_RECORDER_CONNECTION;
+import static android.hardware.hdmi.HdmiControlManager.MESSAGE_RECORDING_ANALOGUE_SERVICE;
+import static android.hardware.hdmi.HdmiControlManager.MESSAGE_RECORDING_CURRENTLY_SELECTED_SOURCE;
+import static android.hardware.hdmi.HdmiControlManager.MESSAGE_RECORDING_DIGITAL_SERVICE;
+import static android.hardware.hdmi.HdmiControlManager.MESSAGE_RECORDING_EXTERNAL_INPUT;
+import static android.hardware.hdmi.HdmiControlManager.MESSAGE_RECORDING_STATUS_MESSAGE_START;
+
+import android.util.Slog;
+
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
/**
- * Feature action that performs one touch record.
- * This class only provides a skeleton of one touch play and has no detail implementaion.
+ * Feature action that performs one touch record. This class only provides a skeleton of one touch
+ * play and has no detail implementation.
*/
public class OneTouchRecordAction extends FeatureAction {
- private final int mRecorderAddress;
+ private static final String TAG = "OneTouchRecordAction";
- OneTouchRecordAction(HdmiCecLocalDevice source, int recorderAddress) {
+ // Timer out for waiting <Record Status>
+ private static final int RECORD_STATUS_TIMEOUT = 120000;
+
+ // State that waits for <Record Status> once sending <Record On>
+ private static final int STATE_WAITING_FOR_RECORD_STATUS = 1;
+ // State that describes recording in progress.
+ private static final int STATE_RECORDING_IN_PROGRESS = 2;
+
+ private final int mRecorderAddress;
+ private final byte[] mRecordSource;
+
+ OneTouchRecordAction(HdmiCecLocalDevice source, int recorderAddress, byte[] recordSource) {
super(source);
mRecorderAddress = recorderAddress;
+ mRecordSource = recordSource;
}
@Override
boolean start() {
- return false;
+ sendRecordOn();
+ return true;
+ }
+
+ private void sendRecordOn() {
+ sendCommand(HdmiCecMessageBuilder.buildRecordOn(getSourceAddress(), mRecorderAddress,
+ mRecordSource),
+ new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ // if failed to send <Record On>, display error message and finish action.
+ if (error != Constants.SEND_RESULT_SUCCESS) {
+ tv().displayOsd(MESSAGE_NO_RECORDING_CHECK_RECORDER_CONNECTION);
+ finish();
+ return;
+ }
+
+ mState = STATE_WAITING_FOR_RECORD_STATUS;
+ addTimer(mState, RECORD_STATUS_TIMEOUT);
+ }
+ });
}
@Override
boolean processCommand(HdmiCecMessage cmd) {
+ if (mState != STATE_WAITING_FOR_RECORD_STATUS) {
+ return false;
+ }
+
+ switch (cmd.getOpcode()) {
+ case Constants.MESSAGE_RECORD_STATUS:
+ return handleRecordStatus(cmd);
+
+ }
return false;
}
+ private boolean handleRecordStatus(HdmiCecMessage cmd) {
+ // Only handle message coming from original recorder.
+ if (cmd.getSource() != mRecorderAddress) {
+ return false;
+ }
+
+ int recordStatus = cmd.getParams()[0];
+ Slog.i(TAG, "Got record status:" + recordStatus + " from " + cmd.getSource());
+
+ int recordStatusMessageCode = recordStatus + MESSAGE_RECORDING_STATUS_MESSAGE_START;
+ tv().displayOsd(recordStatusMessageCode);
+
+ // If recording started successfully, change state and keep this action until <Record Off>
+ // received. Otherwise, finish action.
+ switch (recordStatusMessageCode) {
+ case MESSAGE_RECORDING_CURRENTLY_SELECTED_SOURCE:
+ case MESSAGE_RECORDING_DIGITAL_SERVICE:
+ case MESSAGE_RECORDING_ANALOGUE_SERVICE:
+ case MESSAGE_RECORDING_EXTERNAL_INPUT:
+ mState = STATE_RECORDING_IN_PROGRESS;
+ mActionTimer.clearTimerMessage();
+ break;
+ default:
+ finish();
+ break;
+ }
+ return true;
+ }
+
@Override
void handleTimerEvent(int state) {
+ if (mState != state) {
+ Slog.w(TAG, "Timeout in invalid state:[Expected:" + mState + ", Actual:" + state + "]");
+ return;
+ }
+
+ tv().displayOsd(MESSAGE_NO_RECORDING_CHECK_RECORDER_CONNECTION);
+ finish();
}
int getRecorderAddress() {
diff --git a/services/core/java/com/android/server/job/JobMapReadFinishedListener.java b/services/core/java/com/android/server/job/JobMapReadFinishedListener.java
deleted file mode 100644
index f3e77e6..0000000
--- a/services/core/java/com/android/server/job/JobMapReadFinishedListener.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.job;
-
-import java.util.List;
-
-import com.android.server.job.controllers.JobStatus;
-
-/**
- * Callback definition for I/O thread to let the JobManagerService know when
- * I/O read has completed. Done this way so we don't stall the main thread on
- * boot.
- */
-public interface JobMapReadFinishedListener {
-
- /**
- * Called by the {@link JobStore} at boot, when the disk read is finished.
- */
- public void onJobMapReadFinished(List<JobStatus> jobs);
-}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 3b52baf..587f596 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -69,7 +69,7 @@
* @hide
*/
public class JobSchedulerService extends com.android.server.SystemService
- implements StateChangedListener, JobCompletedListener, JobMapReadFinishedListener {
+ implements StateChangedListener, JobCompletedListener {
// TODO: Switch this off for final version.
static final boolean DEBUG = true;
/** The number of concurrent jobs we run at one time. */
@@ -487,28 +487,6 @@
mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
}
- /**
- * Disk I/O is finished, take the list of jobs we read from disk and add them to our
- * {@link JobStore}.
- * This is run on the {@link com.android.server.IoThread} instance, which is a separate thread,
- * and is called once at boot.
- */
- @Override
- public void onJobMapReadFinished(List<JobStatus> jobs) {
- synchronized (mJobs) {
- for (int i=0; i<jobs.size(); i++) {
- JobStatus js = jobs.get(i);
- if (mJobs.containsJobIdForUid(js.getJobId(), js.getUid())) {
- // An app with BOOT_COMPLETED *might* have decided to reschedule their job, in
- // the same amount of time it took us to read it from disk. If this is the case
- // we leave it be.
- continue;
- }
- startTrackingJob(js);
- }
- }
- }
-
private class JobHandler extends Handler {
public JobHandler(Looper looper) {
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 48312b0..46f557f 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -88,19 +88,26 @@
synchronized (sSingletonLock) {
if (sSingleton == null) {
sSingleton = new JobStore(jobManagerService.getContext(),
- Environment.getDataDirectory(), jobManagerService);
+ Environment.getDataDirectory());
}
return sSingleton;
}
}
+ /**
+ * @return A freshly initialized job store object, with no loaded jobs.
+ */
@VisibleForTesting
- public static JobStore initAndGetForTesting(Context context, File dataDir,
- JobMapReadFinishedListener callback) {
- return new JobStore(context, dataDir, callback);
+ public static JobStore initAndGetForTesting(Context context, File dataDir) {
+ JobStore jobStoreUnderTest = new JobStore(context, dataDir);
+ jobStoreUnderTest.clear();
+ return jobStoreUnderTest;
}
- private JobStore(Context context, File dataDir, JobMapReadFinishedListener callback) {
+ /**
+ * Construct the instance of the job store. This results in a blocking read from disk.
+ */
+ private JobStore(Context context, File dataDir) {
mContext = context;
mDirtyOperations = 0;
@@ -111,7 +118,7 @@
mJobSet = new ArraySet<JobStatus>();
- readJobMapFromDiskAsync(callback);
+ readJobMapFromDisk(mJobSet);
}
/**
@@ -249,12 +256,9 @@
}
}
- private void readJobMapFromDiskAsync(JobMapReadFinishedListener callback) {
- mIoHandler.post(new ReadJobMapFromDiskRunnable(callback));
- }
-
- public void readJobMapFromDisk(JobMapReadFinishedListener callback) {
- new ReadJobMapFromDiskRunnable(callback).run();
+ @VisibleForTesting
+ public void readJobMapFromDisk(ArraySet<JobStatus> jobSet) {
+ new ReadJobMapFromDiskRunnable(jobSet).run();
}
/**
@@ -398,13 +402,18 @@
}
/**
- * Runnable that reads list of persisted job from xml.
- * NOTE: This Runnable locks on JobStore.this
+ * Runnable that reads list of persisted job from xml. This is run once at start up, so doesn't
+ * need to go through {@link JobStore#add(com.android.server.job.controllers.JobStatus)}.
*/
private class ReadJobMapFromDiskRunnable implements Runnable {
- private JobMapReadFinishedListener mCallback;
- public ReadJobMapFromDiskRunnable(JobMapReadFinishedListener callback) {
- mCallback = callback;
+ private final ArraySet<JobStatus> jobSet;
+
+ /**
+ * @param jobSet Reference to the (empty) set of JobStatus objects that back the JobStore,
+ * so that after disk read we can populate it directly.
+ */
+ ReadJobMapFromDiskRunnable(ArraySet<JobStatus> jobSet) {
+ this.jobSet = jobSet;
}
@Override
@@ -414,11 +423,13 @@
FileInputStream fis = mJobsFile.openRead();
synchronized (JobStore.this) {
jobs = readJobMapImpl(fis);
+ if (jobs != null) {
+ for (int i=0; i<jobs.size(); i++) {
+ this.jobSet.add(jobs.get(i));
+ }
+ }
}
fis.close();
- if (jobs != null) {
- mCallback.onJobMapReadFinished(jobs);
- }
} catch (FileNotFoundException e) {
if (JobSchedulerService.DEBUG) {
Slog.d(TAG, "Could not find jobs file, probably there was nothing to load.");
@@ -673,4 +684,4 @@
return Pair.create(earliestRunTimeElapsed, latestRunTimeElapsed);
}
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index b3419c1..058a23e 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -38,6 +38,7 @@
import android.location.Criteria;
import android.location.FusedBatchOptions;
import android.location.GpsMeasurementsEvent;
+import android.location.GpsNavigationMessageEvent;
import android.location.IGpsGeofenceHardware;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
@@ -322,8 +323,16 @@
private GpsStatusListenerHelper mListenerHelper = new GpsStatusListenerHelper() {
@Override
protected boolean isSupported() {
- return native_is_measurement_supported();
+ return GpsLocationProvider.isSupported();
}
+
+ @Override
+ protected boolean registerWithService() {
+ return true;
+ }
+
+ @Override
+ protected void unregisterFromService() {}
};
// Handler for processing events
@@ -374,16 +383,34 @@
}
@Override
- protected void onFirstListenerAdded() {
- native_start_measurement_collection();
+ protected boolean registerWithService() {
+ return native_start_measurement_collection();
}
@Override
- protected void onLastListenerRemoved() {
+ protected void unregisterFromService() {
native_stop_measurement_collection();
}
};
+ private final GpsNavigationMessageProvider mGpsNavigationMessageProvider =
+ new GpsNavigationMessageProvider() {
+ @Override
+ protected boolean isSupported() {
+ return native_is_navigation_message_supported();
+ }
+
+ @Override
+ protected boolean registerWithService() {
+ return native_start_navigation_message_collection();
+ }
+
+ @Override
+ protected void unregisterFromService() {
+ native_stop_navigation_message_collection();
+ }
+ };
+
public IGpsStatusProvider getGpsStatusProvider() {
return mGpsStatusProvider;
}
@@ -396,6 +423,10 @@
return mGpsMeasurementsProvider;
}
+ public GpsNavigationMessageProvider getGpsNavigationMessageProvider() {
+ return mGpsNavigationMessageProvider;
+ }
+
private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -1357,13 +1388,20 @@
}
/**
- * called from native code - Gps Data callback
+ * called from native code - Gps measurements callback
*/
private void reportMeasurementData(GpsMeasurementsEvent event) {
mGpsMeasurementsProvider.onMeasurementsAvailable(event);
}
/**
+ * called from native code - GPS navigation message callback
+ */
+ private void reportNavigationMessage(GpsNavigationMessageEvent event) {
+ mGpsNavigationMessageProvider.onNavigationMessageAvailable(event);
+ }
+
+ /**
* called from native code to inform us what the GPS engine capabilities are
*/
private void setEngineCapabilities(int capabilities) {
@@ -1954,6 +1992,11 @@
// Gps Hal measurements support.
private static native boolean native_is_measurement_supported();
- private static native boolean native_start_measurement_collection();
- private static native boolean native_stop_measurement_collection();
+ private native boolean native_start_measurement_collection();
+ private native boolean native_stop_measurement_collection();
+
+ // Gps Navigation message support.
+ private static native boolean native_is_navigation_message_supported();
+ private native boolean native_start_navigation_message_collection();
+ private native boolean native_stop_navigation_message_collection();
}
diff --git a/services/core/java/com/android/server/location/GpsMeasurementsProvider.java b/services/core/java/com/android/server/location/GpsMeasurementsProvider.java
index 001f638..1c48257 100644
--- a/services/core/java/com/android/server/location/GpsMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GpsMeasurementsProvider.java
@@ -29,6 +29,9 @@
*/
public abstract class GpsMeasurementsProvider
extends RemoteListenerHelper<IGpsMeasurementsListener> {
+ public GpsMeasurementsProvider() {
+ super("GpsMeasurementsProvider");
+ }
public void onMeasurementsAvailable(final GpsMeasurementsEvent event) {
ListenerOperation<IGpsMeasurementsListener> operation =
diff --git a/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java b/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java
new file mode 100644
index 0000000..fca7378
--- /dev/null
+++ b/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.location;
+
+import android.location.GpsNavigationMessageEvent;
+import android.location.IGpsNavigationMessageListener;
+import android.os.RemoteException;
+
+/**
+ * An base implementation for GPS navigation messages provider.
+ * It abstracts out the responsibility of handling listeners, while still allowing technology
+ * specific implementations to be built.
+ *
+ * @hide
+ */
+public abstract class GpsNavigationMessageProvider
+ extends RemoteListenerHelper<IGpsNavigationMessageListener> {
+ public GpsNavigationMessageProvider() {
+ super("GpsNavigationMessageProvider");
+ }
+
+ public void onNavigationMessageAvailable(final GpsNavigationMessageEvent event) {
+ ListenerOperation<IGpsNavigationMessageListener> operation =
+ new ListenerOperation<IGpsNavigationMessageListener>() {
+ @Override
+ public void execute(IGpsNavigationMessageListener listener)
+ throws RemoteException {
+ listener.onGpsNavigationMessageReceived(event);
+ }
+ };
+
+ foreach(operation);
+ }
+}
diff --git a/services/core/java/com/android/server/location/GpsStatusListenerHelper.java b/services/core/java/com/android/server/location/GpsStatusListenerHelper.java
index b741e75..27cf3d8 100644
--- a/services/core/java/com/android/server/location/GpsStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GpsStatusListenerHelper.java
@@ -23,6 +23,10 @@
* Implementation of a handler for {@link IGpsStatusListener}.
*/
abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusListener> {
+ public GpsStatusListenerHelper() {
+ super("GpsStatusListenerHelper");
+ }
+
public void onFirstFix(final int timeToFirstFix) {
Operation operation = new Operation() {
@Override
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index 79335f7..451af18 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -32,16 +32,19 @@
* A helper class, that handles operations in remote listeners, and tracks for remote process death.
*/
abstract class RemoteListenerHelper<TListener extends IInterface> {
- private static final String TAG = "RemoteListenerHelper";
-
+ private final String mTag;
private final HashMap<IBinder, LinkedListener> mListenerMap =
new HashMap<IBinder, LinkedListener>();
+ protected RemoteListenerHelper(String name) {
+ Preconditions.checkNotNull(name);
+ mTag = name;
+ }
+
public boolean addListener(@NonNull TListener listener) {
Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
-
if (!isSupported()) {
- Log.e(TAG, "Refused to add listener, the feature is not supported.");
+ Log.e(mTag, "Refused to add listener, the feature is not supported.");
return false;
}
@@ -58,13 +61,17 @@
} catch (RemoteException e) {
// if the remote process registering the listener is already death, just swallow the
// exception and continue
- Log.e(TAG, "Remote listener already died.", e);
+ Log.e(mTag, "Remote listener already died.", e);
return false;
}
mListenerMap.put(binder, deathListener);
if (mListenerMap.size() == 1) {
- onFirstListenerAdded();
+ if (!registerWithService()) {
+ Log.e(mTag, "RegisterWithService failed, listener will be removed.");
+ removeListener(listener);
+ return false;
+ }
}
}
@@ -73,9 +80,8 @@
public boolean removeListener(@NonNull TListener listener) {
Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
-
if (!isSupported()) {
- Log.e(TAG, "Refused to remove listener, the feature is not supported.");
+ Log.e(mTag, "Refused to remove listener, the feature is not supported.");
return false;
}
@@ -84,26 +90,19 @@
synchronized (mListenerMap) {
linkedListener = mListenerMap.remove(binder);
if (mListenerMap.isEmpty() && linkedListener != null) {
- onLastListenerRemoved();
+ unregisterFromService();
}
}
if (linkedListener != null) {
binder.unlinkToDeath(linkedListener, 0 /* flags */);
}
-
return true;
}
protected abstract boolean isSupported();
-
- protected void onFirstListenerAdded() {
- // event triggered when the first listener has been added
- }
-
- protected void onLastListenerRemoved() {
- // event triggered when the last listener has bee removed
- }
+ protected abstract boolean registerWithService();
+ protected abstract void unregisterFromService();
protected interface ListenerOperation<TListener extends IInterface> {
void execute(TListener listener) throws RemoteException;
@@ -121,7 +120,7 @@
try {
operation.execute(listener);
} catch (RemoteException e) {
- Log.e(TAG, "Error in monitored listener.", e);
+ Log.e(mTag, "Error in monitored listener.", e);
removeListener(listener);
}
}
@@ -141,7 +140,7 @@
@Override
public void binderDied() {
- Log.d(TAG, "Remote Listener died: " + mListener);
+ Log.d(mTag, "Remote Listener died: " + mListener);
removeListener(mListener);
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index ede3dab..e9df507 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -19,22 +19,29 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.media.routing.IMediaRouter;
-import android.media.routing.IMediaRouterDelegate;
-import android.media.routing.IMediaRouterStateCallback;
-import android.media.session.ISessionController;
-import android.media.session.ISessionControllerCallback;
-import android.media.session.ISession;
-import android.media.session.ISessionCallback;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
-import android.media.session.MediaSessionInfo;
-import android.media.session.PlaybackState;
-import android.media.session.ParcelableVolumeInfo;
+import android.content.pm.ParceledListSlice;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
+import android.media.routing.IMediaRouter;
+import android.media.routing.IMediaRouterDelegate;
+import android.media.routing.IMediaRouterStateCallback;
+import android.media.session.ISession;
+import android.media.session.ISessionCallback;
+import android.media.session.ISessionController;
+import android.media.session.ISessionControllerCallback;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.MediaSessionInfo;
+import android.media.session.ParcelableVolumeInfo;
+import android.media.session.PlaybackState;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.MediaMetadata;
+import android.media.Rating;
+import android.media.VolumeProvider;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.DeadObjectException;
@@ -45,15 +52,12 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
-import android.text.TextUtils;
import android.util.Log;
-import android.util.Pair;
import android.util.Slog;
import android.view.KeyEvent;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.List;
import java.util.UUID;
/**
@@ -98,16 +102,19 @@
// TransportPerformer fields
+ private Bundle mExtras;
private MediaMetadata mMetadata;
private PlaybackState mPlaybackState;
+ private ParceledListSlice mQueue;
+ private CharSequence mQueueTitle;
private int mRatingType;
private long mLastActiveTime;
// End TransportPerformer fields
// Volume handling fields
+ private AudioAttributes mAudioAttrs;
private AudioManager mAudioManager;
private int mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL;
- private int mAudioStream = AudioManager.STREAM_MUSIC;
private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
private int mMaxVolume = 0;
private int mCurrentVolume = 0;
@@ -131,6 +138,7 @@
mService = service;
mHandler = new MessageHandler(handler.getLooper());
mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
+ mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
}
/**
@@ -219,7 +227,8 @@
direction = -1;
}
if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
- mAudioManager.adjustStreamVolume(mAudioStream, direction, flags);
+ int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
+ mAudioManager.adjustStreamVolume(stream, direction, flags);
} else {
if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
// Nothing to do, the volume cannot be changed
@@ -245,7 +254,8 @@
public void setVolumeTo(int value, int flags) {
if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
- mAudioManager.setStreamVolume(mAudioStream, value, flags);
+ int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
+ mAudioManager.setStreamVolume(stream, value, flags);
} else {
if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
// Nothing to do. The volume can't be set directly.
@@ -315,8 +325,8 @@
*
* @return The audio stream the session is using.
*/
- public int getAudioStream() {
- return mAudioStream;
+ public AudioAttributes getAudioAttributes() {
+ return mAudioAttrs;
}
/**
@@ -448,6 +458,63 @@
}
}
+ private void pushQueueUpdate() {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ cb.onQueueChanged(mQueue);
+ } catch (DeadObjectException e) {
+ mControllerCallbacks.remove(i);
+ Log.w(TAG, "Removed dead callback in pushQueueUpdate.", e);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushQueueUpdate.", e);
+ }
+ }
+ }
+ }
+
+ private void pushQueueTitleUpdate() {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ cb.onQueueTitleChanged(mQueueTitle);
+ } catch (DeadObjectException e) {
+ mControllerCallbacks.remove(i);
+ Log.w(TAG, "Removed dead callback in pushQueueTitleUpdate.", e);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushQueueTitleUpdate.", e);
+ }
+ }
+ }
+ }
+
+ private void pushExtrasUpdate() {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ cb.onExtrasChanged(mExtras);
+ } catch (DeadObjectException e) {
+ mControllerCallbacks.remove(i);
+ Log.w(TAG, "Removed dead callback in pushExtrasUpdate.", e);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushExtrasUpdate.", e);
+ }
+ }
+ }
+ }
+
private void pushVolumeUpdate() {
synchronized (mLock) {
if (mDestroyed) {
@@ -602,6 +669,24 @@
}
@Override
+ public void setQueue(ParceledListSlice queue) {
+ mQueue = queue;
+ mHandler.post(MessageHandler.MSG_UPDATE_QUEUE);
+ }
+
+ @Override
+ public void setQueueTitle(CharSequence title) {
+ mQueueTitle = title;
+ mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE);
+ }
+
+ @Override
+ public void setExtras(Bundle extras) {
+ mExtras = extras;
+ mHandler.post(MessageHandler.MSG_UPDATE_EXTRAS);
+ }
+
+ @Override
public void setRatingType(int type) {
mRatingType = type;
}
@@ -613,36 +698,34 @@
}
@Override
- public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException {
- boolean typeChanged = type != mVolumeType;
- switch(type) {
- case MediaSession.PLAYBACK_TYPE_LOCAL:
- mVolumeType = type;
- int audioStream = arg1;
- if (isValidStream(audioStream)) {
- mAudioStream = audioStream;
- } else {
- Log.e(TAG, "Cannot set stream to " + audioStream + ". Using music stream");
- mAudioStream = AudioManager.STREAM_MUSIC;
- }
- break;
- case MediaSession.PLAYBACK_TYPE_REMOTE:
- mVolumeType = type;
- mVolumeControlType = arg1;
- mMaxVolume = arg2;
- break;
- default:
- throw new IllegalArgumentException("Volume handling type " + type
- + " not recognized.");
+ public void setPlaybackToLocal(AudioAttributes attributes) {
+ boolean typeChanged;
+ synchronized (mLock) {
+ typeChanged = mVolumeType == MediaSession.PLAYBACK_TYPE_REMOTE;
+ mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL;
+ if (attributes != null) {
+ mAudioAttrs = attributes;
+ } else {
+ Log.e(TAG, "Received null audio attributes, using existing attributes");
+ }
}
if (typeChanged) {
mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this);
}
}
- private boolean isValidStream(int stream) {
- return stream >= AudioManager.STREAM_VOICE_CALL
- && stream <= AudioManager.STREAM_NOTIFICATION;
+ @Override
+ public void setPlaybackToRemote(int control, int max) {
+ boolean typeChanged;
+ synchronized (mLock) {
+ typeChanged = mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL;
+ mVolumeType = MediaSession.PLAYBACK_TYPE_REMOTE;
+ mVolumeControlType = control;
+ mMaxVolume = max;
+ }
+ if (typeChanged) {
+ mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this);
+ }
}
}
@@ -665,14 +748,22 @@
return false;
}
- public void sendCommand(String command, Bundle extras, ResultReceiver cb) {
+ public void sendCommand(String command, Bundle args, ResultReceiver cb) {
try {
- mCb.onCommand(command, extras, cb);
+ mCb.onCommand(command, args, cb);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in sendCommand.", e);
}
}
+ public void sendCustomAction(String action, Bundle args) {
+ try {
+ mCb.onCustomAction(action, args);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in sendCustomAction.", e);
+ }
+ }
+
public void play() {
try {
mCb.onPlay();
@@ -681,6 +772,30 @@
}
}
+ public void playUri(Uri uri, Bundle extras) {
+ try {
+ mCb.onPlayUri(uri, extras);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in playUri.", e);
+ }
+ }
+
+ public void playFromSearch(String query, Bundle extras) {
+ try {
+ mCb.onPlayFromSearch(query, extras);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in playFromSearch.", e);
+ }
+ }
+
+ public void skipToTrack(long id) {
+ try {
+ mCb.onSkipToTrack(id);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in skipToTrack", e);
+ }
+ }
+
public void pause() {
try {
mCb.onPause();
@@ -764,9 +879,9 @@
class ControllerStub extends ISessionController.Stub {
@Override
- public void sendCommand(String command, Bundle extras, ResultReceiver cb)
+ public void sendCommand(String command, Bundle args, ResultReceiver cb)
throws RemoteException {
- mSessionCb.sendCommand(command, extras, cb);
+ mSessionCb.sendCommand(command, args, cb);
}
@Override
@@ -822,11 +937,12 @@
current = mOptimisticVolume != -1 ? mOptimisticVolume
: mCurrentVolume;
} else {
+ int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
type = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
- max = mAudioManager.getStreamMaxVolume(mAudioStream);
- current = mAudioManager.getStreamVolume(mAudioStream);
+ max = mAudioManager.getStreamMaxVolume(stream);
+ current = mAudioManager.getStreamVolume(stream);
}
- return new ParcelableVolumeInfo(mVolumeType, mAudioStream, type, max, current);
+ return new ParcelableVolumeInfo(mVolumeType, mAudioAttrs, type, max, current);
}
}
@@ -856,6 +972,22 @@
}
@Override
+ public void playUri(Uri uri, Bundle extras) throws RemoteException {
+ mSessionCb.playUri(uri, extras);
+ }
+
+ @Override
+ public void playFromSearch(String query, Bundle extras) throws RemoteException {
+ mSessionCb.playFromSearch(query, extras);
+ }
+
+ @Override
+ public void skipToTrack(long id) {
+ mSessionCb.skipToTrack(id);
+ }
+
+
+ @Override
public void pause() throws RemoteException {
mSessionCb.pause();
}
@@ -895,6 +1027,12 @@
mSessionCb.rate(rating);
}
+ @Override
+ public void sendCustomAction(String action, Bundle args)
+ throws RemoteException {
+ mSessionCb.sendCustomAction(action, args);
+ }
+
@Override
public MediaMetadata getMetadata() {
@@ -907,6 +1045,21 @@
}
@Override
+ public ParceledListSlice getQueue() {
+ return mQueue;
+ }
+
+ @Override
+ public CharSequence getQueueTitle() {
+ return mQueueTitle;
+ }
+
+ @Override
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
public int getRatingType() {
return mRatingType;
}
@@ -927,9 +1080,12 @@
private class MessageHandler extends Handler {
private static final int MSG_UPDATE_METADATA = 1;
private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
- private static final int MSG_SEND_EVENT = 3;
- private static final int MSG_UPDATE_SESSION_STATE = 4;
- private static final int MSG_UPDATE_VOLUME = 5;
+ private static final int MSG_UPDATE_QUEUE = 3;
+ private static final int MSG_UPDATE_QUEUE_TITLE = 4;
+ private static final int MSG_UPDATE_EXTRAS = 5;
+ private static final int MSG_SEND_EVENT = 6;
+ private static final int MSG_UPDATE_SESSION_STATE = 7;
+ private static final int MSG_UPDATE_VOLUME = 8;
public MessageHandler(Looper looper) {
super(looper);
@@ -943,6 +1099,15 @@
case MSG_UPDATE_PLAYBACK_STATE:
pushPlaybackStateUpdate();
break;
+ case MSG_UPDATE_QUEUE:
+ pushQueueUpdate();
+ break;
+ case MSG_UPDATE_QUEUE_TITLE:
+ pushQueueTitleUpdate();
+ break;
+ case MSG_UPDATE_EXTRAS:
+ pushExtrasUpdate();
+ break;
case MSG_SEND_EVENT:
pushEvent((String) msg.obj, msg.getData());
break;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index a2dd15e..b0ccd62 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -776,8 +776,8 @@
if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0) {
if (DEBUG) {
Log.d(TAG, "No active session to adjust, skipping media only volume event");
- return;
}
+ return;
}
try {
mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, flags,
diff --git a/services/core/java/com/android/server/notification/GroupedNotificationComparator.java b/services/core/java/com/android/server/notification/GroupedNotificationComparator.java
new file mode 100644
index 0000000..608d55c
--- /dev/null
+++ b/services/core/java/com/android/server/notification/GroupedNotificationComparator.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Sorts notifications, accounting for groups and sort keys.
+ */
+public class GroupedNotificationComparator extends NotificationComparator {
+ private static final String TAG = "GroupedNotificationComparator";
+
+ @Override
+ public int compare(NotificationRecord left, NotificationRecord right) {
+ // "recently intrusive" is an ad hoc group that temporarily claims noisy notifications
+ if (left.isRecentlyIntrusive() != right.isRecentlyIntrusive()) {
+ return left.isRecentlyIntrusive() ? -1 : 1;
+ }
+
+ final NotificationRecord leftProxy = left.getRankingProxy();
+ if (leftProxy == null) {
+ throw new RuntimeException("left proxy cannot be null: " + left.getKey());
+ }
+ final NotificationRecord rightProxy = right.getRankingProxy();
+ if (rightProxy == null) {
+ throw new RuntimeException("right proxy cannot be null: " + right.getKey());
+ }
+ final String leftSortKey = left.getNotification().getSortKey();
+ final String rightSortKey = right.getNotification().getSortKey();
+ if (leftProxy != rightProxy) {
+ // between groups, compare proxies
+ return Integer.compare(leftProxy.getAuthoritativeRank(),
+ rightProxy.getAuthoritativeRank());
+ } else if (TextUtils.isEmpty(leftSortKey) || TextUtils.isEmpty(rightSortKey)) {
+ // missing sort keys, use prior rank
+ return Integer.compare(left.getAuthoritativeRank(),
+ right.getAuthoritativeRank());
+ } else {
+ // use sort keys within group
+ return leftSortKey.compareTo(rightSortKey);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 0546a55..ec81fd2 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -18,35 +18,35 @@
import java.util.Comparator;
/**
- * Sorts notificaitons into attention-relelvant order.
+ * Sorts notifications individually into attention-relelvant order.
*/
public class NotificationComparator
implements Comparator<NotificationRecord> {
@Override
- public int compare(NotificationRecord lhs, NotificationRecord rhs) {
- if (lhs.isRecentlyIntrusive() != rhs.isRecentlyIntrusive()) {
- return lhs.isRecentlyIntrusive() ? -1 : 1;
- }
- final int leftPackagePriority = lhs.getPackagePriority();
- final int rightPackagePriority = rhs.getPackagePriority();
+ public int compare(NotificationRecord left, NotificationRecord right) {
+ final int leftPackagePriority = left.getPackagePriority();
+ final int rightPackagePriority = right.getPackagePriority();
if (leftPackagePriority != rightPackagePriority) {
// by priority, high to low
return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
}
- final int leftScore = lhs.sbn.getScore();
- final int rightScore = rhs.sbn.getScore();
+
+ final int leftScore = left.sbn.getScore();
+ final int rightScore = right.sbn.getScore();
if (leftScore != rightScore) {
// by priority, high to low
return -1 * Integer.compare(leftScore, rightScore);
}
- final float leftPeple = lhs.getContactAffinity();
- final float rightPeople = rhs.getContactAffinity();
- if (leftPeple != rightPeople) {
+
+ final float leftPeople = left.getContactAffinity();
+ final float rightPeople = right.getContactAffinity();
+ if (leftPeople != rightPeople) {
// by contact proximity, close to far
- return -1 * Float.compare(leftPeple, rightPeople);
+ return -1 * Float.compare(leftPeople, rightPeople);
}
+
// then break ties by time, most recent first
- return -1 * Long.compare(lhs.getRankingTimeMs(), rhs.getRankingTimeMs());
+ return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f7cb346..a6711c3 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.service.notification.NotificationListenerService.FLAG_DISABLE_HOST_ALERTS;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -71,6 +72,7 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
@@ -121,6 +123,7 @@
static final int MESSAGE_RECONSIDER_RANKING = 4;
static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
static final int MESSAGE_SEND_RANKING_UPDATE = 6;
+ static final int MESSAGE_LISTENER_FLAGS_CHANGED = 7;
static final int LONG_DELAY = 3500; // 3.5 seconds
static final int SHORT_DELAY = 2000; // 2 seconds
@@ -169,6 +172,9 @@
NotificationRecord mSoundNotification;
NotificationRecord mVibrateNotification;
+ private final ArraySet<ManagedServiceInfo> mListenersDisablingAlerts = new ArraySet<>();
+ private int mListenerFlags; // right now, all flags are global
+
// for enabling and disabling notification pulse behavior
private boolean mScreenOn = true;
private boolean mInCall = false;
@@ -463,7 +469,7 @@
public void onSetDisabled(int status) {
synchronized (mNotificationList) {
mDisableNotificationAlerts = (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
- if (mDisableNotificationAlerts) {
+ if (disableNotificationAlerts()) {
// cancel whatever's going on
long identity = Binder.clearCallingIdentity();
try {
@@ -904,6 +910,13 @@
}
}
+ private void updateListenerFlagsLocked() {
+ final int flags = mListenersDisablingAlerts.isEmpty() ? 0 : FLAG_DISABLE_HOST_ALERTS;
+ if (flags == mListenerFlags) return;
+ mListenerFlags = flags;
+ scheduleListenerFlagsChanged(flags);
+ }
+
private final IBinder mService = new INotificationManager.Stub() {
// Toasts
// ============================================================================
@@ -1249,6 +1262,27 @@
}
@Override
+ public void requestFlagsFromListener(INotificationListener token, int flags) {
+ synchronized (mNotificationList) {
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ final boolean disableAlerts = (flags & FLAG_DISABLE_HOST_ALERTS) != 0;
+ if (disableAlerts) {
+ mListenersDisablingAlerts.add(info);
+ } else {
+ mListenersDisablingAlerts.remove(info);
+ }
+ updateListenerFlagsLocked();
+ }
+ }
+
+ @Override
+ public int getFlagsFromListener(INotificationListener token) {
+ synchronized (mNotificationList) {
+ return mListenerFlags;
+ }
+ }
+
+ @Override
public ZenModeConfig getZenModeConfig() {
enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
return mZenModeHelper.getConfig();
@@ -1339,6 +1373,10 @@
return keys.toArray(new String[keys.size()]);
}
+ private boolean disableNotificationAlerts() {
+ return mDisableNotificationAlerts || (mListenerFlags & FLAG_DISABLE_HOST_ALERTS) != 0;
+ }
+
void dumpImpl(PrintWriter pw, DumpFilter filter) {
pw.print("Current Notification Manager state");
if (filter != null) {
@@ -1422,6 +1460,15 @@
pw.println("\n Notification listeners:");
mListeners.dump(pw, filter);
+ pw.print(" mListenerFlags: "); pw.println(mListenerFlags);
+ pw.print(" mListenersDisablingAlerts: (");
+ N = mListenersDisablingAlerts.size();
+ for (int i = 0; i < N; i++) {
+ final ManagedServiceInfo listener = mListenersDisablingAlerts.valueAt(i);
+ if (i > 0) pw.print(',');
+ pw.print(listener.component);
+ }
+ pw.println(')');
}
pw.println("\n Condition providers:");
@@ -1618,7 +1665,7 @@
}
// If we're not supposed to beep, vibrate, etc. then don't.
- if (!mDisableNotificationAlerts
+ if (!disableNotificationAlerts()
&& (!(record.isUpdate
&& (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
&& (record.getUserId() == UserHandle.USER_ALL ||
@@ -1920,6 +1967,17 @@
}
}
+ private void scheduleListenerFlagsChanged(int state) {
+ mHandler.removeMessages(MESSAGE_LISTENER_FLAGS_CHANGED);
+ mHandler.obtainMessage(MESSAGE_LISTENER_FLAGS_CHANGED, state, 0).sendToTarget();
+ }
+
+ private void handleListenerFlagsChanged(int state) {
+ synchronized (mNotificationList) {
+ mListeners.notifyListenerFlagsChangedLocked(state);
+ }
+ }
+
private final class WorkerHandler extends Handler
{
@Override
@@ -1936,6 +1994,9 @@
case MESSAGE_SEND_RANKING_UPDATE:
handleSendRankingUpdate();
break;
+ case MESSAGE_LISTENER_FLAGS_CHANGED:
+ handleListenerFlagsChanged(msg.arg1);
+ break;
}
}
@@ -2366,6 +2427,9 @@
try {
ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
pkg, 0, UserHandle.getCallingUserId());
+ if (ai == null) {
+ throw new SecurityException("Unknown package " + pkg);
+ }
if (!UserHandle.isSameApp(ai.uid, uid)) {
throw new SecurityException("Calling uid " + uid + " gave package"
+ pkg + " which is owned by uid " + ai.uid);
@@ -2442,6 +2506,13 @@
}
}
+ @Override
+ protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
+ if (mListenersDisablingAlerts.remove(removed)) {
+ updateListenerFlagsLocked();
+ }
+ }
+
/**
* asynchronously notify all listeners about a new notification
*/
@@ -2506,6 +2577,20 @@
}
}
+ public void notifyListenerFlagsChangedLocked(final int flags) {
+ for (final ManagedServiceInfo serviceInfo : mServices) {
+ if (!serviceInfo.isEnabledForCurrentProfiles()) {
+ continue;
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyListenerFlagsChanged(serviceInfo, flags);
+ }
+ });
+ }
+ }
+
private void notifyPostedIfUserMatch(final ManagedServiceInfo info,
final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
if (!info.enabledAndUserMatches(sbn.getUserId())) {
@@ -2541,6 +2626,15 @@
Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
}
}
+
+ private void notifyListenerFlagsChanged(ManagedServiceInfo info, int state) {
+ final INotificationListener listener = (INotificationListener) info.service;
+ try {
+ listener.onListenerFlagsChanged(state);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify listener (listener flags): " + listener, ex);
+ }
+ }
}
public static final class DumpFilter {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 088b813..57f3e2d 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -21,6 +21,7 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.service.notification.StatusBarNotification;
+import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.lang.reflect.Array;
@@ -60,7 +61,12 @@
public boolean isUpdate;
private int mPackagePriority;
- NotificationRecord(StatusBarNotification sbn, int score)
+ // The record that ranking should use for comparisons outside the group.
+ private NotificationRecord mRankingProxy;
+ private int mAuthoritativeRank;
+
+ @VisibleForTesting
+ public NotificationRecord(StatusBarNotification sbn, int score)
{
this.sbn = sbn;
this.score = score;
@@ -73,7 +79,9 @@
mRecentlyIntrusive = previous.mRecentlyIntrusive;
mPackagePriority = previous.mPackagePriority;
mIntercept = previous.mIntercept;
+ mRankingProxy = previous.mRankingProxy;
mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
+ // Don't copy mGroupKey, recompute it, in case it has changed
}
public Notification getNotification() { return sbn.getNotification(); }
@@ -89,6 +97,7 @@
+ " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon));
pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.getScore());
pw.println(prefix + " key=" + sbn.getKey());
+ pw.println(prefix + " groupKey=" + getGroupKey());
pw.println(prefix + " contentIntent=" + notification.contentIntent);
pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
pw.println(prefix + " tickerText=" + notification.tickerText);
@@ -145,6 +154,7 @@
pw.println(prefix + " mRecentlyIntrusive=" + mRecentlyIntrusive);
pw.println(prefix + " mPackagePriority=" + mPackagePriority);
pw.println(prefix + " mIntercept=" + mIntercept);
+ pw.println(prefix + " mRankingProxy=" + getRankingProxy().getKey());
pw.println(prefix + " mRankingTimeMs=" + mRankingTimeMs);
}
@@ -241,4 +251,24 @@
}
return sbn.getPostTime();
}
+
+ public NotificationRecord getRankingProxy() {
+ return mRankingProxy;
+ }
+
+ public void setRankingProxy(NotificationRecord proxy) {
+ mRankingProxy = proxy;
+ }
+
+ public void setAuthoritativeRank(int authoritativeRank) {
+ mAuthoritativeRank = authoritativeRank;
+ }
+
+ public int getAuthoritativeRank() {
+ return mAuthoritativeRank;
+ }
+
+ public String getGroupKey() {
+ return sbn.getGroupKey();
+ }
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index fc03c17..d59e17b 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -17,12 +17,12 @@
import android.app.Notification;
import android.content.Context;
+import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
import org.xmlpull.v1.XmlPullParser;
@@ -49,13 +49,13 @@
private static final String ATT_UID = "uid";
private static final String ATT_PRIORITY = "priority";
- private static final String VALUE_HIGH = "high";
-
private final NotificationSignalExtractor[] mSignalExtractors;
- private final NotificationComparator mRankingComparator = new NotificationComparator();
+ private final NotificationComparator mPreliminaryComparator = new NotificationComparator();
+ private final NotificationComparator mFinalComparator = new GroupedNotificationComparator();
// Package name to uid, to priority. Would be better as Table<String, Int, Int>
private final ArrayMap<String, SparseIntArray> mPackagePriorities;
+ private final ArrayMap<String, NotificationRecord> mProxyByGroupTmp;
private final Context mContext;
private final Handler mRankingHandler;
@@ -83,6 +83,7 @@
Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
}
}
+ mProxyByGroupTmp = new ArrayMap<String, NotificationRecord>();
}
public void extractSignals(NotificationRecord r) {
@@ -166,11 +167,39 @@
}
public void sort(ArrayList<NotificationRecord> notificationList) {
- Collections.sort(notificationList, mRankingComparator);
+ final int N = notificationList.size();
+ // clear group proxies
+ for (int i = N - 1; i >= 0; i--) {
+ notificationList.get(i).setRankingProxy(null);
+ }
+
+ // rank each record individually
+ Collections.sort(notificationList, mPreliminaryComparator);
+
+ // record inidivdual ranking result and nominate proxies for each group
+ for (int i = N - 1; i >= 0; i--) {
+ final NotificationRecord record = notificationList.get(i);
+ record.setAuthoritativeRank(i);
+ final String groupKey = record.getGroupKey();
+ boolean isGroupSummary = record.getNotification().getGroup() != null
+ && (record.getNotification().flags & Notification.FLAG_GROUP_SUMMARY) != 0;
+ if (isGroupSummary || mProxyByGroupTmp.get(groupKey) == null) {
+ mProxyByGroupTmp.put(groupKey, record);
+ }
+ }
+ // assign nominated proxies to each notification
+ for (int i = 0; i < N; i++) {
+ final NotificationRecord record = notificationList.get(i);
+ record.setRankingProxy(mProxyByGroupTmp.get(record.getGroupKey()));
+ }
+ // Do a second ranking pass, using group proxies
+ Collections.sort(notificationList, mFinalComparator);
+
+ mProxyByGroupTmp.clear();
}
public int indexOf(ArrayList<NotificationRecord> notificationList, NotificationRecord target) {
- return Collections.binarySearch(notificationList, target, mRankingComparator);
+ return Collections.binarySearch(notificationList, target, mFinalComparator);
}
private static int safeInt(XmlPullParser parser, String att, int defValue) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 8e625c5..a896550 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -90,9 +90,6 @@
"com.android.mms",
"com.android.example.notificationshowcase"
));
- private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList(
- "com.google.android.deskclock"
- ));
private static final Set<String> SYSTEM_PACKAGES = new HashSet<String>(Arrays.asList(
"android",
"com.android.systemui"
@@ -313,7 +310,8 @@
}
private boolean isAlarm(NotificationRecord record) {
- return ALARM_PACKAGES.contains(record.sbn.getPackageName());
+ return record.isCategory(Notification.CATEGORY_ALARM)
+ || record.isCategory(Notification.CATEGORY_EVENT);
}
private boolean isCall(NotificationRecord record) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a70dd1b..b96f672 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -138,7 +138,6 @@
import android.os.Bundle;
import android.os.Environment;
import android.os.Environment.UserEnvironment;
-import android.os.FileObserver;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
@@ -235,7 +234,6 @@
private static final boolean DEBUG_PACKAGE_INFO = false;
private static final boolean DEBUG_INTENT_MATCHING = false;
private static final boolean DEBUG_PACKAGE_SCANNING = false;
- private static final boolean DEBUG_APP_DIR_OBSERVER = false;
private static final boolean DEBUG_VERIFY = false;
private static final boolean DEBUG_DEXOPT = false;
private static final boolean DEBUG_ABI_SELECTION = false;
@@ -249,12 +247,6 @@
// Cap the size of permission trees that 3rd party apps can define
private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; // characters of text
- private static final int REMOVE_EVENTS =
- FileObserver.CLOSE_WRITE | FileObserver.DELETE | FileObserver.MOVED_FROM;
- private static final int ADD_EVENTS =
- FileObserver.CLOSE_WRITE /*| FileObserver.CREATE*/ | FileObserver.MOVED_TO;
-
- private static final int OBSERVER_EVENTS = REMOVE_EVENTS | ADD_EVENTS;
// Suffix used during package installation when copying/moving
// package apks to install directory.
private static final String INSTALL_PACKAGE_SUFFIX = "-";
@@ -342,30 +334,6 @@
/** The location for ASEC container files on internal storage. */
final String mAsecInternalPath;
- // This is the object monitoring the framework dir.
- final FileObserver mFrameworkInstallObserver;
-
- // This is the object monitoring the system app dir.
- final FileObserver mSystemInstallObserver;
-
- // This is the object monitoring the privileged system app dir.
- final FileObserver mPrivilegedInstallObserver;
-
- // This is the object monitoring the vendor app dir.
- final FileObserver mVendorInstallObserver;
-
- // This is the object monitoring the vendor overlay package dir.
- final FileObserver mVendorOverlayInstallObserver;
-
- // This is the object monitoring the OEM app dir.
- final FileObserver mOemInstallObserver;
-
- // This is the object monitoring mAppInstallDir.
- final FileObserver mAppInstallObserver;
-
- // This is the object monitoring mDrmAppPrivateInstallDir.
- final FileObserver mDrmAppInstallObserver;
-
// Used for privilege escalation. MUST NOT BE CALLED WITH mPackages
// LOCK HELD. Can be called with mInstallLock held.
final Installer mInstaller;
@@ -474,6 +442,9 @@
final SparseArray<PackageVerificationState> mPendingVerification
= new SparseArray<PackageVerificationState>();
+ /** Set of packages associated with each app op permission. */
+ final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>();
+
final PackageInstallerService mInstallerService;
HashSet<PackageParser.Package> mDeferredDexOpt = null;
@@ -1539,16 +1510,10 @@
// For security and version matching reason, only consider
// overlay packages if they reside in VENDOR_OVERLAY_DIR.
File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
- mVendorOverlayInstallObserver = new AppDirObserver(
- vendorOverlayDir.getPath(), OBSERVER_EVENTS, true, false);
- mVendorOverlayInstallObserver.startWatching();
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode | SCAN_TRUSTED_OVERLAY, 0);
// Find base frameworks (resource packages without code).
- mFrameworkInstallObserver = new AppDirObserver(
- frameworkDir.getPath(), OBSERVER_EVENTS, true, false);
- mFrameworkInstallObserver.startWatching();
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
@@ -1556,18 +1521,12 @@
// Collected privileged system packages.
File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
- mPrivilegedInstallObserver = new AppDirObserver(
- privilegedAppDir.getPath(), OBSERVER_EVENTS, true, true);
- mPrivilegedInstallObserver.startWatching();
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0);
// Collect ordinary system packages.
File systemAppDir = new File(Environment.getRootDirectory(), "app");
- mSystemInstallObserver = new AppDirObserver(
- systemAppDir.getPath(), OBSERVER_EVENTS, true, false);
- mSystemInstallObserver.startWatching();
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
@@ -1578,17 +1537,11 @@
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
- mVendorInstallObserver = new AppDirObserver(
- vendorAppDir.getPath(), OBSERVER_EVENTS, true, false);
- mVendorInstallObserver.startWatching();
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
// Collect all OEM packages.
File oemAppDir = new File(Environment.getOemDirectory(), "app");
- mOemInstallObserver = new AppDirObserver(
- oemAppDir.getPath(), OBSERVER_EVENTS, true, false);
- mOemInstallObserver.startWatching();
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
@@ -1662,14 +1615,8 @@
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
- mAppInstallObserver = new AppDirObserver(
- mAppInstallDir.getPath(), OBSERVER_EVENTS, false, false);
- mAppInstallObserver.startWatching();
scanDirLI(mAppInstallDir, 0, scanMode, 0);
-
- mDrmAppInstallObserver = new AppDirObserver(
- mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false, false);
- mDrmAppInstallObserver.startWatching();
+
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
scanMode, 0);
@@ -1700,9 +1647,6 @@
}
reportSettingsProblem(Log.WARN, msg);
}
- } else {
- mAppInstallObserver = null;
- mDrmAppInstallObserver = null;
}
// Now that we know all of the shared libraries, update all clients to have
@@ -2917,6 +2861,17 @@
}
@Override
+ public String[] getAppOpPermissionPackages(String permissionName) {
+ synchronized (mPackages) {
+ ArraySet<String> pkgs = mAppOpPermissionPackages.get(permissionName);
+ if (pkgs == null) {
+ return null;
+ }
+ return pkgs.toArray(new String[pkgs.size()]);
+ }
+ }
+
+ @Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
@@ -6312,10 +6267,32 @@
* @param apkName the name of the installed package.
*/
private static void setBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) {
- // This is of the form "/system/lib64/<packagename>", "/vendor/lib64/<packagename>"
- // or similar.
- final boolean has64BitLibs = (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists();
- final boolean has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists();
+ final File codeFile = new File(pkg.codePath);
+
+ final boolean has64BitLibs;
+ final boolean has32BitLibs;
+ if (isApkFile(codeFile)) {
+ // Monolithic install
+ has64BitLibs = (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists();
+ has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists();
+ } else {
+ // Cluster install
+ final File rootDir = new File(codeFile, LIB_DIR_NAME);
+ if (!ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS)
+ && !TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) {
+ final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]);
+ has64BitLibs = (new File(rootDir, isa)).exists();
+ } else {
+ has64BitLibs = false;
+ }
+ if (!ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS)
+ && !TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) {
+ final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]);
+ has32BitLibs = (new File(rootDir, isa)).exists();
+ } else {
+ has32BitLibs = false;
+ }
+ }
if (has64BitLibs && !has32BitLibs) {
// The package has 64 bit libs, but not 32 bit libs. Its primary
@@ -6569,6 +6546,31 @@
r.append(p.info.name);
}
}
+ if ((p.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+ ArraySet<String> appOpPerms = mAppOpPermissionPackages.get(p.info.name);
+ if (appOpPerms != null) {
+ appOpPerms.remove(pkg.packageName);
+ }
+ }
+ }
+ if (r != null) {
+ if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
+ }
+
+ N = pkg.requestedPermissions.size();
+ r = null;
+ for (i=0; i<N; i++) {
+ String perm = pkg.requestedPermissions.get(i);
+ BasePermission bp = mSettings.mPermissions.get(perm);
+ if (bp != null && (bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+ ArraySet<String> appOpPerms = mAppOpPermissionPackages.get(perm);
+ if (appOpPerms != null) {
+ appOpPerms.remove(pkg.packageName);
+ if (appOpPerms.isEmpty()) {
+ mAppOpPermissionPackages.remove(perm);
+ }
+ }
+ }
}
if (r != null) {
if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
@@ -6753,6 +6755,15 @@
final String perm = bp.name;
boolean allowed;
boolean allowedSig = false;
+ if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+ // Keep track of app op permissions.
+ ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name);
+ if (pkgs == null) {
+ pkgs = new ArraySet<>();
+ mAppOpPermissionPackages.put(bp.name, pkgs);
+ }
+ pkgs.add(pkg.packageName);
+ }
final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
if (level == PermissionInfo.PROTECTION_NORMAL
|| level == PermissionInfo.PROTECTION_DANGEROUS) {
@@ -6815,7 +6826,9 @@
+ " (protectionLevel=" + bp.protectionLevel
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+ ")");
- } else {
+ } else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) {
+ // Don't print warning for app op permissions, since it is fine for them
+ // not to be granted, there is a UI for the user to decide.
Slog.w(TAG, "Not granting permission " + perm
+ " to package " + pkg.packageName
+ " (protectionLevel=" + bp.protectionLevel
@@ -7651,131 +7664,6 @@
}
}
}
-
- private final class AppDirObserver extends FileObserver {
- public AppDirObserver(String path, int mask, boolean isrom, boolean isPrivileged) {
- super(path, mask);
- mRootDir = path;
- mIsRom = isrom;
- mIsPrivileged = isPrivileged;
- }
-
- public void onEvent(int event, String path) {
- String removedPackage = null;
- int removedAppId = -1;
- int[] removedUsers = null;
- String addedPackage = null;
- int addedAppId = -1;
- int[] addedUsers = null;
-
- // TODO post a message to the handler to obtain serial ordering
- synchronized (mInstallLock) {
- String fullPathStr = null;
- File fullPath = null;
- if (path != null) {
- fullPath = new File(mRootDir, path);
- fullPathStr = fullPath.getPath();
- }
-
- if (DEBUG_APP_DIR_OBSERVER)
- Log.v(TAG, "File " + fullPathStr + " changed: " + Integer.toHexString(event));
-
- if (!isApkFile(fullPath)) {
- if (DEBUG_APP_DIR_OBSERVER)
- Log.v(TAG, "Ignoring change of non-package file: " + fullPathStr);
- return;
- }
-
- // Ignore packages that are being installed or
- // have just been installed.
- if (ignoreCodePath(fullPathStr)) {
- return;
- }
- PackageParser.Package p = null;
- PackageSetting ps = null;
- // reader
- synchronized (mPackages) {
- p = mAppDirs.get(fullPathStr);
- if (p != null) {
- ps = mSettings.mPackages.get(p.applicationInfo.packageName);
- if (ps != null) {
- removedUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
- } else {
- removedUsers = sUserManager.getUserIds();
- }
- }
- addedUsers = sUserManager.getUserIds();
- }
- if ((event&REMOVE_EVENTS) != 0) {
- if (ps != null) {
- if (DEBUG_REMOVE) Slog.d(TAG, "Package disappeared: " + ps);
- removePackageLI(ps, true);
- removedPackage = ps.name;
- removedAppId = ps.appId;
- }
- }
-
- if ((event&ADD_EVENTS) != 0) {
- if (p == null) {
- if (DEBUG_INSTALL) Slog.d(TAG, "New file appeared: " + fullPath);
- int flags = PackageParser.PARSE_CHATTY | PackageParser.PARSE_MUST_BE_APK;
- if (mIsRom) {
- flags |= PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR;
- if (mIsPrivileged) {
- flags |= PackageParser.PARSE_IS_PRIVILEGED;
- }
- }
- try {
- p = scanPackageLI(fullPath, flags,
- SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
- System.currentTimeMillis(), UserHandle.ALL, null);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to scan " + fullPath + ": " + e.getMessage());
- p = null;
- }
- if (p != null) {
- /*
- * TODO this seems dangerous as the package may have
- * changed since we last acquired the mPackages
- * lock.
- */
- // writer
- synchronized (mPackages) {
- updatePermissionsLPw(p.packageName, p,
- p.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0);
- }
- addedPackage = p.applicationInfo.packageName;
- addedAppId = UserHandle.getAppId(p.applicationInfo.uid);
- }
- }
- }
-
- // reader
- synchronized (mPackages) {
- mSettings.writeLPr();
- }
- }
-
- if (removedPackage != null) {
- Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, removedAppId);
- extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null, removedUsers);
- }
- if (addedPackage != null) {
- Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, addedAppId);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage,
- extras, null, null, addedUsers);
- }
- }
-
- private final String mRootDir;
- private final boolean mIsRom;
- private final boolean mIsPrivileged;
- }
@Override
public void installPackage(String originPath, IPackageInstallObserver2 observer, int flags,
@@ -7860,7 +7748,7 @@
}
@Override
- public boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked,
+ public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
PackageSetting pkgSetting;
@@ -7868,11 +7756,11 @@
if (UserHandle.getUserId(uid) != userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "setApplicationBlockedSetting for user " + userId);
+ "setApplicationHiddenSetting for user " + userId);
}
- if (blocked && isPackageDeviceAdmin(packageName, userId)) {
- Slog.w(TAG, "Not blocking package " + packageName + ": has active device admin");
+ if (hidden && isPackageDeviceAdmin(packageName, userId)) {
+ Slog.w(TAG, "Not hiding package " + packageName + ": has active device admin");
return false;
}
@@ -7886,10 +7774,10 @@
if (pkgSetting == null) {
return false;
}
- if (pkgSetting.getBlocked(userId) != blocked) {
- pkgSetting.setBlocked(blocked, userId);
+ if (pkgSetting.getHidden(userId) != hidden) {
+ pkgSetting.setHidden(hidden, userId);
mSettings.writePackageRestrictionsLPr(userId);
- if (blocked) {
+ if (hidden) {
sendRemoved = true;
} else {
sendAdded = true;
@@ -7902,8 +7790,8 @@
}
if (sendRemoved) {
killApplication(packageName, UserHandle.getUid(userId, pkgSetting.appId),
- "blocking pkg");
- sendPackageBlockedForUser(packageName, pkgSetting, userId);
+ "hiding pkg");
+ sendApplicationHiddenForUser(packageName, pkgSetting, userId);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -7911,7 +7799,7 @@
return false;
}
- private void sendPackageBlockedForUser(String packageName, PackageSetting pkgSetting,
+ private void sendApplicationHiddenForUser(String packageName, PackageSetting pkgSetting,
int userId) {
final PackageRemovedInfo info = new PackageRemovedInfo();
info.removedPackage = packageName;
@@ -7922,13 +7810,13 @@
/**
* Returns true if application is not found or there was an error. Otherwise it returns
- * the blocked state of the package for the given user.
+ * the hidden state of the package for the given user.
*/
@Override
- public boolean getApplicationBlockedSettingAsUser(String packageName, int userId) {
+ public boolean getApplicationHiddenSettingAsUser(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
enforceCrossUserPermission(Binder.getCallingUid(), userId, true,
- "getApplicationBlocked for user " + userId);
+ "getApplicationHidden for user " + userId);
PackageSetting pkgSetting;
long callingId = Binder.clearCallingIdentity();
try {
@@ -7938,7 +7826,7 @@
if (pkgSetting == null) {
return true;
}
- return pkgSetting.getBlocked(userId);
+ return pkgSetting.getHidden(userId);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -7972,7 +7860,7 @@
}
if (!pkgSetting.getInstalled(userId)) {
pkgSetting.setInstalled(true, userId);
- pkgSetting.setBlocked(false, userId);
+ pkgSetting.setHidden(false, userId);
mSettings.writePackageRestrictionsLPr(userId);
sendAdded = true;
}
@@ -10571,19 +10459,19 @@
return;
}
- boolean blocked = false;
+ boolean uninstallBlocked = false;
if ((flags & PackageManager.DELETE_ALL_USERS) != 0) {
int[] users = sUserManager.getUserIds();
for (int i = 0; i < users.length; ++i) {
if (getBlockUninstallForUser(packageName, users[i])) {
- blocked = true;
+ uninstallBlocked = true;
break;
}
}
} else {
- blocked = getBlockUninstallForUser(packageName, userId);
+ uninstallBlocked = getBlockUninstallForUser(packageName, userId);
}
- if (blocked) {
+ if (uninstallBlocked) {
try {
observer.packageDeleted(packageName, PackageManager.DELETE_FAILED_OWNER_BLOCKED);
} catch (RemoteException re) {
@@ -10591,7 +10479,9 @@
return;
}
- if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageAsUser: pkg=" + packageName + " user=" + userId);
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, "deletePackageAsUser: pkg=" + packageName + " user=" + userId);
+ }
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {
public void run() {
@@ -11010,7 +10900,7 @@
false, //installed
true, //stopped
true, //notLaunched
- false, //blocked
+ false, //hidden
null, null, null,
false // blockUninstall
);
@@ -12402,6 +12292,22 @@
if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
mSettings.dumpPermissionsLPr(pw, packageName, dumpState);
+ if (packageName == null) {
+ for (int iperm=0; iperm<mAppOpPermissionPackages.size(); iperm++) {
+ if (iperm == 0) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("AppOp Permissions:");
+ }
+ pw.print(" AppOp Permission ");
+ pw.print(mAppOpPermissionPackages.keyAt(iperm));
+ pw.println(":");
+ ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm);
+ for (int ipkg=0; ipkg<pkgs.size(); ipkg++) {
+ pw.print(" "); pw.println(pkgs.valueAt(ipkg));
+ }
+ }
+ }
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index e164f5f..e29332c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -281,12 +281,12 @@
modifyUserState(userId).notLaunched = stop;
}
- boolean getBlocked(int userId) {
- return readUserState(userId).blocked;
+ boolean getHidden(int userId) {
+ return readUserState(userId).hidden;
}
- void setBlocked(boolean blocked, int userId) {
- modifyUserState(userId).blocked = blocked;
+ void setHidden(boolean hidden, int userId) {
+ modifyUserState(userId).hidden = hidden;
}
boolean getBlockUninstall(int userId) {
@@ -298,7 +298,7 @@
}
void setUserState(int userId, int enabled, boolean installed, boolean stopped,
- boolean notLaunched, boolean blocked,
+ boolean notLaunched, boolean hidden,
String lastDisableAppCaller, HashSet<String> enabledComponents,
HashSet<String> disabledComponents, boolean blockUninstall) {
PackageUserState state = modifyUserState(userId);
@@ -306,7 +306,7 @@
state.installed = installed;
state.stopped = stopped;
state.notLaunched = notLaunched;
- state.blocked = blocked;
+ state.hidden = hidden;
state.lastDisableAppCaller = lastDisableAppCaller;
state.enabledComponents = enabledComponents;
state.disabledComponents = disabledComponents;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index e16894e..d1ea3e1 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -153,7 +153,10 @@
private static final String ATTR_ENABLED = "enabled";
private static final String ATTR_ENABLED_CALLER = "enabledCaller";
private static final String ATTR_STOPPED = "stopped";
+ // Legacy, here for reading older versions of the package-restrictions.
private static final String ATTR_BLOCKED = "blocked";
+ // New name for the above attribute.
+ private static final String ATTR_HIDDEN = "hidden";
private static final String ATTR_INSTALLED = "inst";
private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
@@ -606,7 +609,7 @@
installed,
true, // stopped,
true, // notLaunched
- false, // blocked
+ false, // hidden
null, null, null,
false // blockUninstall
);
@@ -1177,7 +1180,7 @@
true, // installed
false, // stopped
false, // notLaunched
- false, // blocked
+ false, // hidden
null, null, null,
false // blockUninstall
);
@@ -1233,9 +1236,14 @@
final String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED);
final boolean stopped = stoppedStr == null
? false : Boolean.parseBoolean(stoppedStr);
+ // For backwards compatibility with the previous name of "blocked", which
+ // now means hidden, read the old attribute as well.
final String blockedStr = parser.getAttributeValue(null, ATTR_BLOCKED);
- final boolean blocked = blockedStr == null
+ boolean hidden = blockedStr == null
? false : Boolean.parseBoolean(blockedStr);
+ final String hiddenStr = parser.getAttributeValue(null, ATTR_HIDDEN);
+ hidden = hiddenStr == null
+ ? hidden : Boolean.parseBoolean(hiddenStr);
final String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED);
final boolean notLaunched = stoppedStr == null
? false : Boolean.parseBoolean(notLaunchedStr);
@@ -1263,7 +1271,7 @@
}
}
- ps.setUserState(userId, enabled, installed, stopped, notLaunched, blocked,
+ ps.setUserState(userId, enabled, installed, stopped, notLaunched, hidden,
enabledCaller, enabledComponents, disabledComponents, blockUninstall);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
@@ -1432,7 +1440,7 @@
PackageUserState ustate = pkg.readUserState(userId);
if (ustate.stopped || ustate.notLaunched || !ustate.installed
|| ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT
- || ustate.blocked
+ || ustate.hidden
|| (ustate.enabledComponents != null
&& ustate.enabledComponents.size() > 0)
|| (ustate.disabledComponents != null
@@ -1451,8 +1459,8 @@
if (ustate.notLaunched) {
serializer.attribute(null, ATTR_NOT_LAUNCHED, "true");
}
- if (ustate.blocked) {
- serializer.attribute(null, ATTR_BLOCKED, "true");
+ if (ustate.hidden) {
+ serializer.attribute(null, ATTR_HIDDEN, "true");
}
if (ustate.blockUninstall) {
serializer.attribute(null, ATTR_BLOCK_UNINSTALL, "true");
@@ -3443,7 +3451,7 @@
pw.print(user.id);
pw.print(",");
pw.print(ps.getInstalled(user.id) ? "I" : "i");
- pw.print(ps.getBlocked(user.id) ? "B" : "b");
+ pw.print(ps.getHidden(user.id) ? "B" : "b");
pw.print(ps.getStopped(user.id) ? "S" : "s");
pw.print(ps.getNotLaunched(user.id) ? "l" : "L");
pw.print(",");
@@ -3583,8 +3591,8 @@
pw.print(prefix); pw.print(" User "); pw.print(user.id); pw.print(": ");
pw.print(" installed=");
pw.print(ps.getInstalled(user.id));
- pw.print(" blocked=");
- pw.print(ps.getBlocked(user.id));
+ pw.print(" hidden=");
+ pw.print(ps.getHidden(user.id));
pw.print(" stopped=");
pw.print(ps.getStopped(user.id));
pw.print(" notLaunched=");
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b4cc252..33ecc4d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1428,7 +1428,7 @@
removeRestrictionsForUser(userHandle, true);
}
- private void removeRestrictionsForUser(final int userHandle, boolean unblockApps) {
+ private void removeRestrictionsForUser(final int userHandle, boolean unhideApps) {
synchronized (mPackagesLock) {
// Remove all user restrictions
setUserRestrictions(new Bundle(), userHandle);
@@ -1437,12 +1437,12 @@
// Remove any app restrictions
cleanAppRestrictions(userHandle);
}
- if (unblockApps) {
- unblockAllAppsForUser(userHandle);
+ if (unhideApps) {
+ unhideAllInstalledAppsForUser(userHandle);
}
}
- private void unblockAllAppsForUser(final int userHandle) {
+ private void unhideAllInstalledAppsForUser(final int userHandle) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -1453,8 +1453,8 @@
try {
for (ApplicationInfo appInfo : apps) {
if ((appInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0
- && (appInfo.flags & ApplicationInfo.FLAG_BLOCKED) != 0) {
- mPm.setApplicationBlockedSettingAsUser(appInfo.packageName, false,
+ && (appInfo.flags & ApplicationInfo.FLAG_HIDDEN) != 0) {
+ mPm.setApplicationHiddenSettingAsUser(appInfo.packageName, false,
userHandle);
}
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index e272f38..2b4a24a 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -110,8 +110,9 @@
// True if a user activity message should be sent.
private boolean mUserActivityPending;
- // True if the screen on blocker has been acquired.
- private boolean mScreenOnBlockerAcquired;
+ // The currently active screen on listener. This field is non-null whenever the
+ // ScreenOnBlocker has been acquired and we are awaiting a callback to release it.
+ private ScreenOnUnblocker mPendingScreenOnUnblocker;
public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
IAppOpsService appOps, SuspendBlocker suspendBlocker, ScreenOnBlocker screenOnBlocker,
@@ -255,15 +256,16 @@
if (mActualPowerState != POWER_STATE_AWAKE) {
mActualPowerState = POWER_STATE_AWAKE;
mPendingWakeUpBroadcast = true;
- if (!mScreenOnBlockerAcquired) {
- mScreenOnBlockerAcquired = true;
+ if (mPendingScreenOnUnblocker == null) {
mScreenOnBlocker.acquire();
}
+ final ScreenOnUnblocker unblocker = new ScreenOnUnblocker();
+ mPendingScreenOnUnblocker = unblocker;
mHandler.post(new Runnable() {
@Override
public void run() {
EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
- mPolicy.wakingUp(mScreenOnListener);
+ mPolicy.wakingUp(unblocker);
mActivityManagerInternal.wakingUp();
}
});
@@ -459,18 +461,17 @@
}
}
- private final WindowManagerPolicy.ScreenOnListener mScreenOnListener =
- new WindowManagerPolicy.ScreenOnListener() {
+ private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {
@Override
public void onScreenOn() {
synchronized (mLock) {
- if (mScreenOnBlockerAcquired && !mPendingWakeUpBroadcast) {
- mScreenOnBlockerAcquired = false;
+ if (mPendingScreenOnUnblocker == this) {
+ mPendingScreenOnUnblocker = null;
mScreenOnBlocker.release();
}
}
}
- };
+ }
private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() {
@Override
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4b1e8eb..8c52fad 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -404,6 +404,12 @@
// Use NaN to disable.
private float mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = Float.NaN;
+ // The screen state to use while dozing.
+ private int mDozeScreenStateOverrideFromDreamManager = Display.STATE_UNKNOWN;
+
+ // The screen brightness to use while dozing.
+ private int mDozeScreenBrightnessOverrideFromDreamManager = PowerManager.BRIGHTNESS_DEFAULT;
+
// Time when we last logged a warning about calling userActivity() without permission.
private long mLastWarningAboutUserActivityPermission = Long.MIN_VALUE;
@@ -1370,11 +1376,12 @@
if (mUserActivitySummary == 0
&& mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
- if (now < nextTimeout
- && mDisplayPowerRequest.wantScreenOnNormal()) {
- mUserActivitySummary = mDisplayPowerRequest.screenState
- == DisplayPowerRequest.SCREEN_STATE_BRIGHT ?
- USER_ACTIVITY_SCREEN_BRIGHT : USER_ACTIVITY_SCREEN_DIM;
+ if (now < nextTimeout) {
+ if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT) {
+ mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
+ } else if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
+ mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
+ }
}
}
if (mUserActivitySummary != 0) {
@@ -1631,7 +1638,7 @@
if (mWakefulness != WAKEFULNESS_DREAMING
|| !mDreamsSupportedConfig
|| !mDreamsEnabledSetting
- || !mDisplayPowerRequest.wantScreenOnNormal()
+ || !mDisplayPowerRequest.isBrightOrDim()
|| !mBootCompleted) {
return false;
}
@@ -1672,8 +1679,7 @@
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
| DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
| DIRTY_SETTINGS | DIRTY_SCREEN_ON_BLOCKER_RELEASED)) != 0) {
- final int newScreenState = getDesiredScreenPowerStateLocked();
- mDisplayPowerRequest.screenState = newScreenState;
+ mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();
int screenBrightness = mScreenBrightnessSettingDefault;
float screenAutoBrightnessAdjustment = 0.0f;
@@ -1713,13 +1719,22 @@
mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled;
+ if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
+ mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
+ mDisplayPowerRequest.dozeScreenBrightness =
+ mDozeScreenBrightnessOverrideFromDreamManager;
+ } else {
+ mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
+ mDisplayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+ }
+
mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
mRequestWaitForNegativeProximity);
mRequestWaitForNegativeProximity = false;
if (DEBUG_SPEW) {
Slog.d(TAG, "updateScreenStateLocked: mDisplayReady=" + mDisplayReady
- + ", newScreenState=" + newScreenState
+ + ", policy=" + mDisplayPowerRequest.policy
+ ", mWakefulness=" + mWakefulness
+ ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
+ ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
@@ -1737,22 +1752,22 @@
return value >= -1.0f && value <= 1.0f;
}
- private int getDesiredScreenPowerStateLocked() {
+ private int getDesiredScreenPolicyLocked() {
if (mWakefulness == WAKEFULNESS_ASLEEP) {
- return DisplayPowerRequest.SCREEN_STATE_OFF;
+ return DisplayPowerRequest.POLICY_OFF;
}
if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
- return DisplayPowerRequest.SCREEN_STATE_DOZE;
+ return DisplayPowerRequest.POLICY_DOZE;
}
if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
|| (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
|| !mBootCompleted) {
- return DisplayPowerRequest.SCREEN_STATE_BRIGHT;
+ return DisplayPowerRequest.POLICY_BRIGHT;
}
- return DisplayPowerRequest.SCREEN_STATE_DIM;
+ return DisplayPowerRequest.POLICY_DIM;
}
private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks =
@@ -1895,7 +1910,7 @@
if (!mDisplayReady) {
return true;
}
- if (mDisplayPowerRequest.wantScreenOnNormal()) {
+ if (mDisplayPowerRequest.isBrightOrDim()) {
// If we asked for the screen to be on but it is off due to the proximity
// sensor then we may suspend but only if the configuration allows it.
// On some hardware it may not be safe to suspend because the proximity
@@ -2103,6 +2118,19 @@
}
}
+ private void setDozeOverrideFromDreamManagerInternal(
+ int screenState, int screenBrightness) {
+ synchronized (mLock) {
+ if (mDozeScreenStateOverrideFromDreamManager != screenState
+ || mDozeScreenBrightnessOverrideFromDreamManager != screenBrightness) {
+ mDozeScreenStateOverrideFromDreamManager = screenState;
+ mDozeScreenBrightnessOverrideFromDreamManager = screenBrightness;
+ mDirty |= DIRTY_SETTINGS;
+ updatePowerStateLocked();
+ }
+ }
+ }
+
private void powerHintInternal(int hintId, int data) {
nativeSendPowerHint(hintId, data);
}
@@ -2257,6 +2285,10 @@
+ mTemporaryScreenBrightnessSettingOverride);
pw.println(" mTemporaryScreenAutoBrightnessAdjustmentSettingOverride="
+ mTemporaryScreenAutoBrightnessAdjustmentSettingOverride);
+ pw.println(" mDozeScreenStateOverrideFromDreamManager="
+ + mDozeScreenStateOverrideFromDreamManager);
+ pw.println(" mDozeScreenBrightnessOverrideFromDreamManager="
+ + mDozeScreenBrightnessOverrideFromDreamManager);
pw.println(" mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum);
pw.println(" mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum);
pw.println(" mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault);
@@ -3026,63 +3058,44 @@
}
private final class LocalService extends PowerManagerInternal {
- /**
- * Used by the window manager to override the screen brightness based on the
- * current foreground activity.
- *
- * This method must only be called by the window manager.
- *
- * @param brightness The overridden brightness, or -1 to disable the override.
- */
@Override
- public void setScreenBrightnessOverrideFromWindowManager(int brightness) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.DEVICE_POWER, null);
-
- final long ident = Binder.clearCallingIdentity();
- try {
- setScreenBrightnessOverrideFromWindowManagerInternal(brightness);
- } finally {
- Binder.restoreCallingIdentity(ident);
+ public void setScreenBrightnessOverrideFromWindowManager(int screenBrightness) {
+ if (screenBrightness < PowerManager.BRIGHTNESS_DEFAULT
+ || screenBrightness > PowerManager.BRIGHTNESS_ON) {
+ screenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
}
+ setScreenBrightnessOverrideFromWindowManagerInternal(screenBrightness);
}
- /**
- * Used by the window manager to override the button brightness based on the
- * current foreground activity.
- *
- * This method must only be called by the window manager.
- *
- * @param brightness The overridden brightness, or -1 to disable the override.
- */
@Override
- public void setButtonBrightnessOverrideFromWindowManager(int brightness) {
+ public void setButtonBrightnessOverrideFromWindowManager(int screenBrightness) {
// Do nothing.
// Button lights are not currently supported in the new implementation.
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.DEVICE_POWER, null);
}
- /**
- * Used by the window manager to override the user activity timeout based on the
- * current foreground activity. It can only be used to make the timeout shorter
- * than usual, not longer.
- *
- * This method must only be called by the window manager.
- *
- * @param timeoutMillis The overridden timeout, or -1 to disable the override.
- */
+ @Override
+ public void setDozeOverrideFromDreamManager(int screenState, int screenBrightness) {
+ switch (screenState) {
+ case Display.STATE_UNKNOWN:
+ case Display.STATE_OFF:
+ case Display.STATE_DOZE:
+ case Display.STATE_DOZE_SUSPEND:
+ case Display.STATE_ON:
+ break;
+ default:
+ screenState = Display.STATE_UNKNOWN;
+ break;
+ }
+ if (screenBrightness < PowerManager.BRIGHTNESS_DEFAULT
+ || screenBrightness > PowerManager.BRIGHTNESS_ON) {
+ screenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+ }
+ setDozeOverrideFromDreamManagerInternal(screenState, screenBrightness);
+ }
+
@Override
public void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.DEVICE_POWER, null);
-
- final long ident = Binder.clearCallingIdentity();
- try {
- setUserActivityTimeoutOverrideFromWindowManagerInternal(timeoutMillis);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ setUserActivityTimeoutOverrideFromWindowManagerInternal(timeoutMillis);
}
@Override
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index c011fd3..c6213f9 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -40,16 +40,17 @@
public final static int ERROR_STALE_CONFIG = -2;
public final static int ERROR_UNKNOWN = -3;
- // Below should be in sync with hardware/libhardware/include/hardware/tv_input.h
public static final int EVENT_DEVICE_AVAILABLE = 1;
public static final int EVENT_DEVICE_UNAVAILABLE = 2;
public static final int EVENT_STREAM_CONFIGURATION_CHANGED = 3;
+ public static final int EVENT_FIRST_FRAME_CAPTURED = 4;
public interface Callback {
public void onDeviceAvailable(
TvInputHardwareInfo info, TvStreamConfig[] configs);
public void onDeviceUnavailable(int deviceId);
public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs);
+ public void onFirstFrameCaptured(int deviceId, int streamId);
}
private native long nativeOpen();
@@ -131,6 +132,11 @@
mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, 0).sendToTarget();
}
+ private void firstFrameCapturedFromNative(int deviceId, int streamId) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, streamId));
+ }
+
// Handler.Callback implementation
private Queue<Message> mPendingMessageQueue = new LinkedList<Message>();
@@ -167,6 +173,13 @@
break;
}
+ case EVENT_FIRST_FRAME_CAPTURED: {
+ int deviceId = msg.arg1;
+ int streamId = msg.arg2;
+ mCallback.onFirstFrameCaptured(deviceId, streamId);
+ break;
+ }
+
default:
Slog.e(TAG, "Unknown event: " + msg);
return false;
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 9433ce4..65e617b 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -21,9 +21,11 @@
import android.content.Context;
import android.hardware.hdmi.HdmiCecDeviceInfo;
-import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiHotplugEvent;
+import android.hardware.hdmi.IHdmiControlService;
import android.hardware.hdmi.IHdmiDeviceEventListener;
+import android.hardware.hdmi.IHdmiHotplugEventListener;
+import android.hardware.hdmi.IHdmiInputChangeListener;
import android.media.AudioDevicePort;
import android.media.AudioManager;
import android.media.AudioPatch;
@@ -39,6 +41,8 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -48,8 +52,10 @@
import com.android.server.SystemService;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -60,20 +66,28 @@
*
* @hide
*/
-class TvInputHardwareManager
- implements TvInputHal.Callback, HdmiControlManager.HotplugEventListener {
+class TvInputHardwareManager implements TvInputHal.Callback {
private static final String TAG = TvInputHardwareManager.class.getSimpleName();
+
+ private final Context mContext;
+ private final Listener mListener;
private final TvInputHal mHal = new TvInputHal(this);
private final SparseArray<Connection> mConnections = new SparseArray<Connection>();
private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>();
- private final Context mContext;
- private final Listener mListener;
- private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>();
+ /* A map from a device ID to the matching TV input ID. */
+ private final SparseArray<String> mHardwareInputIdMap = new SparseArray<String>();
+ /* A map from a HDMI logical address to the matching TV input ID. */
+ private final SparseArray<String> mHdmiCecInputIdMap = new SparseArray<String>();
+ private final Map<String, TvInputInfo> mInputMap = new ArrayMap<String, TvInputInfo>();
+
private final AudioManager mAudioManager;
- private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
- // TODO: Should handle INACTIVE case.
- private final SparseArray<TvInputInfo> mTvInputInfoMap = new SparseArray<TvInputInfo>();
+ private IHdmiControlService mHdmiControlService;
+ private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
+ new HdmiHotplugEventListener();
private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
+ private final IHdmiInputChangeListener mHdmiInputChangeListener = new HdmiInputChangeListener();
+ // TODO: Should handle INACTIVE case.
+ private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
// Calls to mListener should happen here.
private final Handler mHandler = new ListenerHandler();
@@ -89,15 +103,22 @@
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
- HdmiControlManager hdmiControlManager =
- (HdmiControlManager) mContext.getSystemService(Context.HDMI_CONTROL_SERVICE);
- hdmiControlManager.addHotplugEventListener(this);
+ mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(
+ Context.HDMI_CONTROL_SERVICE));
+ if (mHdmiControlService != null) {
+ try {
+ mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
+ mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
+ mHdmiControlService.setInputChangeListener(mHdmiInputChangeListener);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
+ }
+ }
}
}
@Override
- public void onDeviceAvailable(
- TvInputHardwareInfo info, TvStreamConfig[] configs) {
+ public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
synchronized (mLock) {
Connection connection = new Connection(info);
connection.updateConfigsLocked(configs);
@@ -126,8 +147,9 @@
connection.resetLocked(null, null, null, null, null);
mConnections.remove(deviceId);
buildInfoListLocked();
+ TvInputHardwareInfo info = connection.getHardwareInfoLocked();
mHandler.obtainMessage(
- ListenerHandler.HARDWARE_DEVICE_REMOVED, deviceId, 0).sendToTarget();
+ ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
}
}
@@ -144,7 +166,24 @@
try {
connection.getCallbackLocked().onStreamConfigChanged(configs);
} catch (RemoteException e) {
- Slog.e(TAG, "onStreamConfigurationChanged: " + e);
+ Slog.e(TAG, "error in onStreamConfigurationChanged", e);
+ }
+ }
+ }
+
+ @Override
+ public void onFirstFrameCaptured(int deviceId, int streamId) {
+ synchronized (mLock) {
+ Connection connection = mConnections.get(deviceId);
+ if (connection == null) {
+ Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
+ + deviceId);
+ return;
+ }
+ Runnable runnable = connection.getOnFirstFrameCapturedLocked();
+ if (runnable != null) {
+ runnable.run();
+ connection.setOnFirstFrameCapturedLocked(null);
}
}
}
@@ -155,6 +194,17 @@
}
}
+ public List<HdmiCecDeviceInfo> getHdmiCecInputDeviceList() {
+ if (mHdmiControlService != null) {
+ try {
+ return mHdmiControlService.getInputDevices();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in getHdmiCecInputDeviceList", e);
+ }
+ }
+ return Collections.emptyList();
+ }
+
private boolean checkUidChangedLocked(
Connection connection, int callingUid, int resolvedUserId) {
Integer connectionCallingUid = connection.getCallingUidLocked();
@@ -176,17 +226,16 @@
}
}
- public void registerTvInputInfo(TvInputInfo info, int deviceId) {
- if (info.getType() == TvInputInfo.TYPE_VIRTUAL) {
- throw new IllegalArgumentException("info (" + info + ") has virtual type.");
- }
+ public void addHardwareTvInput(int deviceId, TvInputInfo info) {
synchronized (mLock) {
- if (mTvInputInfoMap.indexOfKey(deviceId) >= 0) {
+ String oldInputId = mHardwareInputIdMap.get(deviceId);
+ if (oldInputId != null) {
Slog.w(TAG, "Trying to override previous registration: old = "
- + mTvInputInfoMap.get(deviceId) + ":" + deviceId + ", new = "
+ + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
+ info + ":" + deviceId);
}
- mTvInputInfoMap.put(deviceId, info);
+ mHardwareInputIdMap.put(deviceId, info.getId());
+ mInputMap.put(info.getId(), info);
for (int i = 0; i < mHdmiStateMap.size(); ++i) {
String inputId = findInputIdForHdmiPortLocked(mHdmiStateMap.keyAt(i));
@@ -199,6 +248,50 @@
}
}
+ private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
+ for (int i = 0; i < map.size(); ++i) {
+ if (map.valueAt(i).equals(value)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public void addHdmiCecTvInput(int logicalAddress, TvInputInfo info) {
+ if (info.getType() != TvInputInfo.TYPE_HDMI) {
+ throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
+ }
+ synchronized (mLock) {
+ String parentId = info.getParentId();
+ int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
+ if (parentIndex < 0) {
+ throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
+ }
+ String oldInputId = mHdmiCecInputIdMap.get(logicalAddress);
+ if (oldInputId != null) {
+ Slog.w(TAG, "Trying to override previous registration: old = "
+ + mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = "
+ + info + ":" + logicalAddress);
+ }
+ mHdmiCecInputIdMap.put(logicalAddress, info.getId());
+ mInputMap.put(info.getId(), info);
+ }
+ }
+
+ public void removeTvInput(String inputId) {
+ synchronized (mLock) {
+ mInputMap.remove(inputId);
+ int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
+ if (hardwareIndex >= 0) {
+ mHardwareInputIdMap.removeAt(hardwareIndex);
+ }
+ int cecIndex = indexOfEqualValue(mHdmiCecInputIdMap, inputId);
+ if (cecIndex >= 0) {
+ mHdmiCecInputIdMap.removeAt(cecIndex);
+ }
+ }
+ }
+
/**
* Create a TvInputHardware object with a specific deviceId. One service at a time can access
* the object, and if more than one process attempts to create hardware with the same deviceId,
@@ -254,28 +347,78 @@
for (TvInputHardwareInfo hardwareInfo : mInfoList) {
if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
&& hardwareInfo.getHdmiPortId() == port) {
- TvInputInfo info = mTvInputInfoMap.get(hardwareInfo.getDeviceId());
- return (info == null) ? null : info.getId();
+ return mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
}
}
return null;
}
- // HdmiControlManager.HotplugEventListener implementation.
-
- @Override
- public void onReceived(HdmiHotplugEvent event) {
- String inputId = null;
-
- synchronized (mLock) {
- mHdmiStateMap.put(event.getPort(), event.isConnected());
- inputId = findInputIdForHdmiPortLocked(event.getPort());
- if (inputId == null) {
- return;
+ private int findDeviceIdForInputIdLocked(String inputId) {
+ for (int i = 0; i < mConnections.size(); ++i) {
+ Connection connection = mConnections.get(i);
+ if (connection.getInfoLocked().getId().equals(inputId)) {
+ return i;
}
- mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
- convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
}
+ return -1;
+ }
+
+ /**
+ * Get the list of TvStreamConfig which is buffered mode.
+ */
+ public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
+ int resolvedUserId) {
+ List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
+ synchronized (mLock) {
+ int deviceId = findDeviceIdForInputIdLocked(inputId);
+ if (deviceId < 0) {
+ Slog.e(TAG, "Invalid inputId : " + inputId);
+ return configsList;
+ }
+ Connection connection = mConnections.get(deviceId);
+ for (TvStreamConfig config : connection.getConfigsLocked()) {
+ if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
+ configsList.add(config);
+ }
+ }
+ }
+ return configsList;
+ }
+
+ /**
+ * Take a snapshot of the given TV input into the provided Surface.
+ */
+ public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
+ int callingUid, int resolvedUserId) {
+ synchronized (mLock) {
+ int deviceId = findDeviceIdForInputIdLocked(inputId);
+ if (deviceId < 0) {
+ Slog.e(TAG, "Invalid inputId : " + inputId);
+ return false;
+ }
+ Connection connection = mConnections.get(deviceId);
+ final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
+ if (hardwareImpl != null) {
+ // Stop previous capture.
+ Runnable runnable = connection.getOnFirstFrameCapturedLocked();
+ if (runnable != null) {
+ runnable.run();
+ connection.setOnFirstFrameCapturedLocked(null);
+ }
+
+ boolean result = hardwareImpl.startCapture(surface, config);
+ if (result) {
+ connection.setOnFirstFrameCapturedLocked(new Runnable() {
+ @Override
+ public void run() {
+ hardwareImpl.stopCapture(config);
+ }
+ });
+ }
+ return result;
+ }
+ }
+ return false;
}
private class Connection implements IBinder.DeathRecipient {
@@ -286,6 +429,7 @@
private TvStreamConfig[] mConfigs = null;
private Integer mCallingUid = null;
private Integer mResolvedUserId = null;
+ private Runnable mOnFirstFrameCaptured;
public Connection(TvInputHardwareInfo hardwareInfo) {
mHardwareInfo = hardwareInfo;
@@ -299,7 +443,7 @@
try {
mCallback.onReleased();
} catch (RemoteException e) {
- Slog.e(TAG, "Connection::resetHardware: " + e);
+ Slog.e(TAG, "error in Connection::resetLocked", e);
}
mHardware.release();
}
@@ -308,12 +452,13 @@
mInfo = info;
mCallingUid = callingUid;
mResolvedUserId = resolvedUserId;
+ mOnFirstFrameCaptured = null;
if (mHardware != null && mCallback != null) {
try {
mCallback.onStreamConfigChanged(getConfigsLocked());
} catch (RemoteException e) {
- Slog.e(TAG, "Connection::resetHardware: " + e);
+ Slog.e(TAG, "error in Connection::resetLocked", e);
}
}
}
@@ -334,6 +479,10 @@
return mHardware;
}
+ public TvInputHardwareImpl getHardwareImplLocked() {
+ return mHardware;
+ }
+
public ITvInputHardwareCallback getCallbackLocked() {
return mCallback;
}
@@ -350,6 +499,14 @@
return mResolvedUserId;
}
+ public void setOnFirstFrameCapturedLocked(Runnable runnable) {
+ mOnFirstFrameCaptured = runnable;
+ }
+
+ public Runnable getOnFirstFrameCapturedLocked() {
+ return mOnFirstFrameCaptured;
+ }
+
@Override
public void binderDied() {
synchronized (mLock) {
@@ -427,19 +584,6 @@
if (surface == null && mActiveConfig == null) {
return false;
}
- if (mInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
- if (surface != null) {
- // Set "Active Source" for HDMI.
- // TODO(hdmi): mHdmiClient.deviceSelect(...);
- mActiveHdmiSources.add(mInfo.getDeviceId());
- } else {
- mActiveHdmiSources.remove(mInfo.getDeviceId());
- if (mActiveHdmiSources.size() == 0) {
- // Tell HDMI that no HDMI source is active
- // TODO(hdmi): mHdmiClient.portSelect(null);
- }
- }
- }
if (mAudioSource != null && mAudioSink != null) {
if (surface != null) {
AudioPortConfig sourceConfig = mAudioSource.activeConfig();
@@ -500,12 +644,43 @@
// TODO(hdmi): mHdmiClient.sendKeyEvent(event);
return false;
}
+
+ private boolean startCapture(Surface surface, TvStreamConfig config) {
+ synchronized (mImplLock) {
+ if (mReleased) {
+ return false;
+ }
+ if (surface == null || config == null) {
+ return false;
+ }
+ if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
+ return false;
+ }
+
+ int result = mHal.addStream(mInfo.getDeviceId(), surface, config);
+ return result == TvInputHal.SUCCESS;
+ }
+ }
+
+ private boolean stopCapture(TvStreamConfig config) {
+ synchronized (mImplLock) {
+ if (mReleased) {
+ return false;
+ }
+ if (config == null) {
+ return false;
+ }
+
+ int result = mHal.removeStream(mInfo.getDeviceId(), config);
+ return result == TvInputHal.SUCCESS;
+ }
+ }
}
interface Listener {
public void onStateChanged(String inputId, int state);
public void onHardwareDeviceAdded(TvInputHardwareInfo info);
- public void onHardwareDeviceRemoved(int deviceId);
+ public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
public void onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDevice);
public void onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDevice);
}
@@ -514,8 +689,8 @@
private static final int STATE_CHANGED = 1;
private static final int HARDWARE_DEVICE_ADDED = 2;
private static final int HARDWARE_DEVICE_REMOVED = 3;
- private static final int CEC_DEVICE_ADDED = 4;
- private static final int CEC_DEVICE_REMOVED = 5;
+ private static final int HDMI_CEC_DEVICE_ADDED = 4;
+ private static final int HDMI_CEC_DEVICE_REMOVED = 5;
@Override
public final void handleMessage(Message msg) {
@@ -532,16 +707,16 @@
break;
}
case HARDWARE_DEVICE_REMOVED: {
- int deviceId = msg.arg1;
- mListener.onHardwareDeviceRemoved(deviceId);
+ TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
+ mListener.onHardwareDeviceRemoved(info);
break;
}
- case CEC_DEVICE_ADDED: {
+ case HDMI_CEC_DEVICE_ADDED: {
HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj;
mListener.onHdmiCecDeviceAdded(info);
break;
}
- case CEC_DEVICE_REMOVED: {
+ case HDMI_CEC_DEVICE_REMOVED: {
HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj;
mListener.onHdmiCecDeviceRemoved(info);
break;
@@ -554,13 +729,38 @@
}
}
+ // Listener implementations for HdmiControlService
+
+ private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
+ @Override
+ public void onReceived(HdmiHotplugEvent event) {
+ synchronized (mLock) {
+ mHdmiStateMap.put(event.getPort(), event.isConnected());
+ String inputId = findInputIdForHdmiPortLocked(event.getPort());
+ if (inputId == null) {
+ return;
+ }
+ mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
+ convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
+ }
+ }
+ }
+
private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
@Override
public void onStatusChanged(HdmiCecDeviceInfo deviceInfo, boolean activated) {
mHandler.obtainMessage(
- activated ? ListenerHandler.CEC_DEVICE_ADDED
- : ListenerHandler.CEC_DEVICE_REMOVED,
+ activated ? ListenerHandler.HDMI_CEC_DEVICE_ADDED
+ : ListenerHandler.HDMI_CEC_DEVICE_REMOVED,
0, 0, deviceInfo).sendToTarget();
}
}
+
+ private final class HdmiInputChangeListener extends IHdmiInputChangeListener.Stub {
+ @Override
+ public void onChanged(HdmiCecDeviceInfo device) throws RemoteException {
+ // TODO: Build a channel Uri for the TvInputInfo associated with the logical device
+ // and send an intent to TV app
+ }
+ }
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 0115366..5808e2f 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -51,6 +51,7 @@
import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputService;
+import android.media.tv.TvStreamConfig;
import android.media.tv.TvTrackInfo;
import android.net.Uri;
import android.os.Binder;
@@ -220,7 +221,7 @@
List<ResolveInfo> services = pm.queryIntentServices(
new Intent(TvInputService.SERVICE_INTERFACE),
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
- List<TvInputInfo> infoList = new ArrayList<TvInputInfo>();
+ List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
for (ResolveInfo ri : services) {
ServiceInfo si = ri.serviceInfo;
if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
@@ -229,7 +230,7 @@
continue;
}
try {
- infoList.clear();
+ inputList.clear();
ComponentName service = new ComponentName(si.packageName, si.name);
if (hasHardwarePermission(pm, service)) {
ServiceState serviceState = userState.serviceStateMap.get(service);
@@ -240,13 +241,13 @@
serviceState = new ServiceState(service, userId);
userState.serviceStateMap.put(service, serviceState);
} else {
- infoList.addAll(serviceState.mInputList);
+ inputList.addAll(serviceState.mInputList);
}
} else {
- infoList.add(TvInputInfo.createTvInputInfo(mContext, ri));
+ inputList.add(TvInputInfo.createTvInputInfo(mContext, ri));
}
- for (TvInputInfo info : infoList) {
+ for (TvInputInfo info : inputList) {
if (DEBUG) Slog.d(TAG, "add " + info.getId());
TvInputState state = userState.inputMap.get(info.getId());
if (state == null) {
@@ -370,10 +371,14 @@
}
private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
- SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+ return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId));
+ }
+
+ private ITvInputSession getSessionLocked(SessionState sessionState) {
ITvInputSession session = sessionState.mSession;
if (session == null) {
- throw new IllegalStateException("Session not yet created for token " + sessionToken);
+ throw new IllegalStateException("Session not yet created for token "
+ + sessionState.mSessionToken);
}
return session;
}
@@ -652,8 +657,12 @@
}
private void removeSessionStateLocked(IBinder sessionToken, int userId) {
- // Remove the session state from the global session state map of the current user.
UserState userState = getUserStateLocked(userId);
+ if (sessionToken == userState.mainSessionToken) {
+ userState.mainSessionToken = null;
+ }
+
+ // Remove the session state from the global session state map of the current user.
SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
// Close the open log entry, if any.
@@ -919,6 +928,58 @@
}
@Override
+ public void setMainSession(IBinder sessionToken, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "setMainSession");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ UserState userState = getUserStateLocked(resolvedUserId);
+ if (sessionToken == userState.mainSessionToken) {
+ return;
+ }
+
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ ServiceState serviceState = getServiceStateLocked(
+ sessionState.mInfo.getComponent(), resolvedUserId);
+ ITvInputSession session = getSessionLocked(sessionState);
+
+ ServiceState prevMainServiceState = null;
+ ITvInputSession prevMainSession = null;
+ if (userState.mainSessionToken != null) {
+ SessionState prevMainSessionState = getSessionStateLocked(
+ userState.mainSessionToken, Process.SYSTEM_UID, resolvedUserId);
+ prevMainServiceState = getServiceStateLocked(
+ prevMainSessionState.mInfo.getComponent(), resolvedUserId);
+ prevMainSession = getSessionLocked(prevMainSessionState);
+ }
+
+ userState.mainSessionToken = sessionToken;
+
+ // Inform the new main session first. See {@link TvInputService#onSetMain}.
+ if (serviceState.mIsHardware) {
+ try {
+ session.setMainSession(true);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in setMainSession", e);
+ }
+ }
+ if (prevMainSession != null && prevMainServiceState.mIsHardware) {
+ try {
+ prevMainSession.setMainSession(false);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in setMainSession", e);
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void setSurface(IBinder sessionToken, Surface surface, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
@@ -993,7 +1054,10 @@
synchronized (mLock) {
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
-
+ if (TvContract.isChannelUriForPassthroughTvInput(channelUri)) {
+ // Do not log the watch history for passthrough inputs.
+ return;
+ }
long currentTime = System.currentTimeMillis();
long channelId = ContentUris.parseId(channelUri);
@@ -1116,6 +1180,27 @@
}
@Override
+ public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data,
+ int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "sendAppPrivateCommand");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .appPrivateCommand(command, data);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in sendAppPrivateCommand", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
int userId) {
final int callingUid = Binder.getCallingUid();
@@ -1192,22 +1277,6 @@
}
@Override
- public void registerTvInputInfo(TvInputInfo info, int deviceId) {
- // TODO: Revisit to sort out deviceId ownership.
- if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
- != PackageManager.PERMISSION_GRANTED) {
- return;
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- mTvInputHardwareManager.registerTvInputInfo(info, deviceId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
public ITvInputHardware acquireTvInputHardware(int deviceId,
ITvInputHardwareCallback callback, TvInputInfo info, int userId)
throws RemoteException {
@@ -1249,6 +1318,55 @@
}
@Override
+ public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId)
+ throws RemoteException {
+ if (mContext.checkCallingPermission(
+ android.Manifest.permission.CAPTURE_TV_INPUT)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "getAvailableTvStreamConfigList");
+ try {
+ return mTvInputHardwareManager.getAvailableTvStreamConfigList(
+ inputId, callingUid, resolvedUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config,
+ int userId)
+ throws RemoteException {
+ if (mContext.checkCallingPermission(
+ android.Manifest.permission.CAPTURE_TV_INPUT)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "captureFrame");
+ try {
+ final String wrappedInputId;
+ synchronized (mLock) {
+ UserState userState = getUserStateLocked(resolvedUserId);
+ wrappedInputId = userState.wrappedInputMap.get(inputId);
+ }
+ return mTvInputHardwareManager.captureFrame(
+ (wrappedInputId != null) ? wrappedInputId : inputId,
+ surface, config, callingUid, resolvedUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
@SuppressWarnings("resource")
protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -1418,6 +1536,12 @@
// A set of callbacks.
private final Set<ITvInputManagerCallback> callbackSet =
new HashSet<ITvInputManagerCallback>();
+
+ // A mapping from the TV input id to wrapped input id.
+ private final Map<String, String> wrappedInputMap = new HashMap<String, String>();
+
+ // The token of a "main" TV input session.
+ private IBinder mainSessionToken = null;
}
private final class ClientState implements IBinder.DeathRecipient {
@@ -1529,8 +1653,6 @@
Slog.d(TAG, "onServiceConnected(name=" + name + ")");
}
synchronized (mLock) {
- List<TvInputHardwareInfo> hardwareInfoList =
- mTvInputHardwareManager.getHardwareList();
UserState userState = getUserStateLocked(mUserId);
ServiceState serviceState = userState.serviceStateMap.get(mName);
serviceState.mService = ITvInputService.Stub.asInterface(service);
@@ -1559,6 +1681,8 @@
}
if (serviceState.mIsHardware) {
+ List<TvInputHardwareInfo> hardwareInfoList =
+ mTvInputHardwareManager.getHardwareList();
for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) {
try {
serviceState.mService.notifyHardwareAdded(hardwareInfo);
@@ -1567,7 +1691,15 @@
}
}
- // TODO: Grab CEC devices and notify them to the service.
+ List<HdmiCecDeviceInfo> cecDeviceInfoList =
+ mTvInputHardwareManager.getHdmiCecInputDeviceList();
+ for (HdmiCecDeviceInfo cecDeviceInfo : cecDeviceInfoList) {
+ try {
+ serviceState.mService.notifyHdmiCecDeviceAdded(cecDeviceInfo);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in notifyHdmiCecDeviceAdded", e);
+ }
+ }
}
}
}
@@ -1602,8 +1734,10 @@
for (TvInputState inputState : userState.inputMap.values()) {
if (inputState.mInfo.getComponent().equals(name)) {
- notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
+ String inputId = inputState.mInfo.getId();
+ notifyInputStateChangedLocked(userState, inputId,
INPUT_STATE_DISCONNECTED, null);
+ userState.wrappedInputMap.remove(inputId);
}
}
updateServiceConnectionLocked(mName, mUserId);
@@ -1621,32 +1755,49 @@
mUserId = userId;
}
- @Override
- public void addTvInput(TvInputInfo inputInfo) {
- synchronized (mLock) {
- if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "The caller does not have permission to add a TV input ("
- + inputInfo + ").");
- return;
- }
+ private void ensureHardwarePermission() {
+ if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("The caller does not have hardware permission");
+ }
+ }
- ServiceState serviceState = getServiceStateLocked(mName, mUserId);
- serviceState.mInputList.add(inputInfo);
- buildTvInputListLocked(mUserId);
+ private void ensureValidInput(TvInputInfo inputInfo) {
+ if (inputInfo.getId() == null || !mName.equals(inputInfo.getComponent())) {
+ throw new IllegalArgumentException("Invalid TvInputInfo");
+ }
+ }
+
+ private void addTvInputLocked(TvInputInfo inputInfo) {
+ ServiceState serviceState = getServiceStateLocked(mName, mUserId);
+ serviceState.mInputList.add(inputInfo);
+ buildTvInputListLocked(mUserId);
+ }
+
+ @Override
+ public void addHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
+ ensureHardwarePermission();
+ ensureValidInput(inputInfo);
+ synchronized (mLock) {
+ mTvInputHardwareManager.addHardwareTvInput(deviceId, inputInfo);
+ addTvInputLocked(inputInfo);
+ }
+ }
+
+ @Override
+ public void addHdmiCecTvInput(int logicalAddress, TvInputInfo inputInfo) {
+ ensureHardwarePermission();
+ ensureValidInput(inputInfo);
+ synchronized (mLock) {
+ mTvInputHardwareManager.addHdmiCecTvInput(logicalAddress, inputInfo);
+ addTvInputLocked(inputInfo);
}
}
@Override
public void removeTvInput(String inputId) {
+ ensureHardwarePermission();
synchronized (mLock) {
- if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "The caller does not have permission to remove a TV input ("
- + inputId + ").");
- return;
- }
-
ServiceState serviceState = getServiceStateLocked(mName, mUserId);
boolean removed = false;
for (Iterator<TvInputInfo> it = serviceState.mInputList.iterator();
@@ -1659,11 +1810,33 @@
}
if (removed) {
buildTvInputListLocked(mUserId);
+ mTvInputHardwareManager.removeTvInput(inputId);
} else {
Slog.e(TAG, "TvInputInfo with inputId=" + inputId + " not found.");
}
}
}
+
+ @Override
+ public void setWrappedInputId(String inputId, String wrappedInputId) {
+ synchronized (mLock) {
+ if (!hasInputIdLocked(inputId)) {
+ return;
+ }
+ UserState userState = getUserStateLocked(mUserId);
+ userState.wrappedInputMap.put(inputId, wrappedInputId);
+ }
+ }
+
+ private boolean hasInputIdLocked(String inputId) {
+ ServiceState serviceState = getServiceStateLocked(mName, mUserId);
+ for (TvInputInfo inputInfo : serviceState.mInputList) {
+ if (inputInfo.getId().equals(inputId)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
private final class LogHandler extends Handler {
@@ -1835,14 +2008,14 @@
}
@Override
- public void onHardwareDeviceRemoved(int deviceId) {
+ public void onHardwareDeviceRemoved(TvInputHardwareInfo info) {
synchronized (mLock) {
UserState userState = getUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
if (!serviceState.mIsHardware || serviceState.mService == null) continue;
try {
- serviceState.mService.notifyHardwareRemoved(deviceId);
+ serviceState.mService.notifyHardwareRemoved(info);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHardwareRemoved", e);
}
@@ -1851,16 +2024,34 @@
}
@Override
- public void onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDevice) {
+ public void onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDeviceInfo) {
synchronized (mLock) {
- // TODO
+ UserState userState = getUserStateLocked(mCurrentUserId);
+ // Broadcast the event to all hardware inputs.
+ for (ServiceState serviceState : userState.serviceStateMap.values()) {
+ if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+ try {
+ serviceState.mService.notifyHdmiCecDeviceAdded(cecDeviceInfo);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in notifyHdmiCecDeviceAdded", e);
+ }
+ }
}
}
@Override
- public void onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDevice) {
+ public void onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDeviceInfo) {
synchronized (mLock) {
- // TODO
+ UserState userState = getUserStateLocked(mCurrentUserId);
+ // Broadcast the event to all hardware inputs.
+ for (ServiceState serviceState : userState.serviceStateMap.values()) {
+ if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+ try {
+ serviceState.mService.notifyHdmiCecDeviceRemoved(cecDeviceInfo);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in notifyHdmiCecDeviceRemoved", e);
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
new file mode 100644
index 0000000..64b852b
--- /dev/null
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.view.Display;
+import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.util.Slog;
+
+class CircularDisplayMask {
+ private static final String TAG = "CircularDisplayMask";
+
+ private static final int STROKE_WIDTH = 2;
+ // size of the chin
+ private int mScreenOffset = 0;
+ // Display dimensions
+ private Point mScreenSize;
+
+ private final SurfaceControl mSurfaceControl;
+ private final Surface mSurface = new Surface();
+ private int mLastDW;
+ private int mLastDH;
+ private boolean mDrawNeeded;
+ private Paint mPaint;
+ private int mRotation;
+ private boolean mVisible;
+ private boolean mDimensionsUnequal = false;
+
+ public CircularDisplayMask(Display display, SurfaceSession session, int zOrder,
+ int screenOffset) {
+ mScreenSize = new Point();
+ display.getSize(mScreenSize);
+ if (mScreenSize.x != mScreenSize.y) {
+ Slog.w(TAG, "Screen dimensions of displayId = " + display.getDisplayId() +
+ "are not equal, circularMask will not be drawn.");
+ mDimensionsUnequal = true;
+ }
+
+ SurfaceControl ctrl = null;
+ try {
+ ctrl = new SurfaceControl(session, "CircularDisplayMask",
+ mScreenSize.x, mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+ ctrl.setLayerStack(display.getLayerStack());
+ ctrl.setLayer(zOrder);
+ ctrl.setPosition(0, 0);
+ ctrl.show();
+ mSurface.copyFrom(ctrl);
+ } catch (OutOfResourcesException e) {
+ }
+ mSurfaceControl = ctrl;
+ mDrawNeeded = true;
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setColor(Color.BLACK);
+ mPaint.setStrokeWidth(STROKE_WIDTH);
+ mScreenOffset = screenOffset;
+ }
+
+ private void drawIfNeeded() {
+ if (!mDrawNeeded || !mVisible || mDimensionsUnequal) {
+ return;
+ }
+ mDrawNeeded = false;
+
+ Rect dirty = new Rect(0, 0, mScreenSize.x, mScreenSize.y);
+ Canvas c = null;
+ try {
+ c = mSurface.lockCanvas(dirty);
+ } catch (IllegalArgumentException e) {
+ } catch (Surface.OutOfResourcesException e) {
+ }
+ if (c == null) {
+ return;
+ }
+ c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
+ switch (mRotation) {
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_90:
+ // chin bottom or right
+ mSurfaceControl.setPosition(0, 0);
+ break;
+ case Surface.ROTATION_180:
+ // chin top
+ mSurfaceControl.setPosition(0, -mScreenOffset);
+ break;
+ case Surface.ROTATION_270:
+ // chin left
+ mSurfaceControl.setPosition(-mScreenOffset, 0);
+ break;
+ }
+
+ int circleRadius = mScreenSize.x / 2;
+ c.drawCircle(circleRadius, circleRadius, circleRadius, mPaint);
+ mSurface.unlockCanvasAndPost(c);
+ }
+
+ // Note: caller responsible for being inside
+ // Surface.openTransaction() / closeTransaction()
+ public void setVisibility(boolean on) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+ mVisible = on;
+ drawIfNeeded();
+ if (on) {
+ mSurfaceControl.show();
+ } else {
+ mSurfaceControl.hide();
+ }
+ }
+
+ void positionSurface(int dw, int dh, int rotation) {
+ if (mLastDW == dw && mLastDH == dh && mRotation == rotation) {
+ return;
+ }
+ mLastDW = dw;
+ mLastDH = dh;
+ mDrawNeeded = true;
+ mRotation = rotation;
+ drawIfNeeded();
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fa22ded..170ecbd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.LayoutParams.*;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import android.app.admin.DevicePolicyManager;
import android.app.AppOpsManager;
import android.util.ArraySet;
import android.util.TimeUtils;
@@ -425,6 +426,13 @@
*/
WindowState[] mRebuildTmp = new WindowState[20];
+ /**
+ * Stores for each user whether screencapture is disabled
+ * This array is essentially a cache for all userId for
+ * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled(null, userId)}
+ */
+ SparseArray<Boolean> mScreenCaptureDisabled = new SparseArray<Boolean>();
+
IInputMethodManager mInputMethodManager;
AccessibilityController mAccessibilityController;
@@ -432,6 +440,7 @@
final SurfaceSession mFxSession;
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
+ CircularDisplayMask mCircularDisplayMask;
FocusedStackFrame mFocusedStackFrame;
int mFocusedStackLayer;
@@ -803,14 +812,14 @@
mActivityManager = ActivityManagerNative.getDefault();
mBatteryStats = BatteryStatsService.getService();
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
- mAppOps.startWatchingMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, null,
+ AppOpsManager.OnOpChangedInternalListener opListener =
new AppOpsManager.OnOpChangedInternalListener() {
- @Override
- public void onOpChanged(int op, String packageName) {
+ @Override public void onOpChanged(int op, String packageName) {
updateAppOpsState();
}
- }
- );
+ };
+ mAppOps.startWatchingMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, null, opListener);
+ mAppOps.startWatchingMode(AppOpsManager.OP_TOAST_WINDOW, null, opListener);
// Get persisted window scale setting
mWindowAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(),
@@ -845,6 +854,8 @@
} finally {
SurfaceControl.closeTransaction();
}
+
+ showCircularDisplayMaskIfNeeded();
}
public InputMonitor getInputMonitor() {
@@ -2439,6 +2450,45 @@
return res;
}
+ /**
+ * Returns whether screen capture is disabled for all windows of a specific user.
+ */
+ boolean isScreenCaptureDisabledLocked(int userId) {
+ Boolean disabled = mScreenCaptureDisabled.get(userId);
+ if (disabled != null) {
+ return disabled;
+ }
+
+ // mScreenCaptureDisabled not set yet, try to update it.
+ updateScreenCaptureDisabledLocked(userId);
+ disabled = mScreenCaptureDisabled.get(userId);
+ if (disabled == null) {
+ // Not able to update, return false by default.
+ return false;
+ } else {
+ return disabled;
+ }
+ }
+
+ /**
+ * Update mScreenCaptureDisabled for specific user according to the device policy manager.
+ */
+ @Override
+ public void updateScreenCaptureDisabled(int userId) {
+ mH.sendMessage(mH.obtainMessage(H.UPDATE_SCRN_CAP, userId, 0 /* unused argument */));
+ }
+
+ void updateScreenCaptureDisabledLocked(int userId) {
+ DevicePolicyManager dpm = (DevicePolicyManager) mContext
+ .getSystemService(Context.DEVICE_POLICY_SERVICE);
+ if (dpm != null) {
+ boolean disabled = dpm.getScreenCaptureDisabled(null, userId);
+ mScreenCaptureDisabled.put(userId, disabled);
+ } else {
+ Slog.e(TAG, "Could not get DevicePolicyManager.");
+ }
+ }
+
public void removeWindow(Session session, IWindow client) {
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client, false);
@@ -4852,8 +4902,11 @@
// Where to start adding?
for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
- int pos = findAppWindowInsertionPointLocked(tokens.get(0));
final int numTokens = tokens.size();
+ if (numTokens == 0) {
+ continue;
+ }
+ int pos = findAppWindowInsertionPointLocked(tokens.get(0));
for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
final AppWindowToken wtoken = tokens.get(tokenNdx);
if (wtoken != null) {
@@ -5597,6 +5650,44 @@
}
}
+ public void showCircularDisplayMaskIfNeeded() {
+ // we're fullscreen and not hosted in an ActivityView
+ if (mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_windowIsRound)
+ && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_windowShowCircularMask)) {
+ mH.sendMessage(mH.obtainMessage(H.SHOW_DISPLAY_MASK));
+ }
+ }
+
+ public void showCircularMask() {
+ synchronized(mWindowMap) {
+
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ ">>> OPEN TRANSACTION showDisplayMask");
+ SurfaceControl.openTransaction();
+ try {
+ // TODO(multi-display): support multiple displays
+ if (mCircularDisplayMask == null) {
+ int screenOffset = (int) mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.circular_display_mask_offset);
+
+ mCircularDisplayMask = new CircularDisplayMask(
+ getDefaultDisplayContentLocked().getDisplay(),
+ mFxSession,
+ mPolicy.windowTypeToLayerLw(
+ WindowManager.LayoutParams.TYPE_POINTER)
+ * TYPE_LAYER_MULTIPLIER + 10, screenOffset);
+ }
+ mCircularDisplayMask.setVisibility(true);
+ } finally {
+ SurfaceControl.closeTransaction();
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ "<<< CLOSE TRANSACTION showDisplayMask");
+ }
+ }
+ }
+
// TODO: more accounting of which pid(s) turned it on, keep count,
// only allow disables from pids which have count on, etc.
@Override
@@ -5672,13 +5763,28 @@
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
}
+ final DisplayContent displayContent = getDisplayContentLocked(displayId);
+ if (displayContent == null) {
+ if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken
+ + ": returning null. No Display for displayId=" + displayId);
+ return null;
+ }
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ int dw = displayInfo.logicalWidth;
+ int dh = displayInfo.logicalHeight;
+ if (dw == 0 || dh == 0) {
+ if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken
+ + ": returning null. logical widthxheight=" + dw + "x" + dh);
+ return null;
+ }
+
Bitmap rawss = null;
int maxLayer = 0;
final Rect frame = new Rect();
+ final Rect stackBounds = new Rect();
float scale = 0;
- int dw, dh;
int rot = Surface.ROTATION_0;
boolean screenshotReady;
@@ -5694,7 +5800,15 @@
int retryCount = 0;
WindowState appWin = null;
- do {
+ final boolean appIsImTarget = mInputMethodTarget != null
+ && mInputMethodTarget.mAppToken != null
+ && mInputMethodTarget.mAppToken.appToken != null
+ && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+
+ final int aboveAppLayer = (mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)
+ * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
+
+ while (true) {
if (retryCount++ > 0) {
try {
Thread.sleep(100);
@@ -5702,28 +5816,9 @@
}
}
synchronized(mWindowMap) {
- final DisplayContent displayContent = getDisplayContentLocked(displayId);
- if (displayContent == null) {
- return null;
- }
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- dw = displayInfo.logicalWidth;
- dh = displayInfo.logicalHeight;
-
- int aboveAppLayer = mPolicy.windowTypeToLayerLw(TYPE_APPLICATION)
- * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
- aboveAppLayer += TYPE_LAYER_MULTIPLIER;
-
- boolean isImeTarget = mInputMethodTarget != null
- && mInputMethodTarget.mAppToken != null
- && mInputMethodTarget.mAppToken.appToken != null
- && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
-
// Figure out the part of the screen that is actually the app.
- boolean including = false;
appWin = null;
final WindowList windows = displayContent.getWindowList();
- final Rect stackBounds = new Rect();
for (int i = windows.size() - 1; i >= 0; i--) {
WindowState ws = windows.get(i);
if (!ws.mHasSurface) {
@@ -5732,27 +5827,22 @@
if (ws.mLayer >= aboveAppLayer) {
continue;
}
- // When we will skip windows: when we are not including
- // ones behind a window we didn't skip, and we are actually
- // taking a screenshot of a specific app.
- if (!including && appToken != null) {
- // Also, we can possibly skip this window if it is not
- // an IME target or the application for the screenshot
- // is not the current IME target.
- if (!ws.mIsImWindow || !isImeTarget) {
- // And finally, this window is of no interest if it
- // is not associated with the screenshot app.
- if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
- continue;
- }
- appWin = ws;
- ws.getStackBounds(stackBounds);
+ if (ws.mIsImWindow) {
+ if (!appIsImTarget) {
+ continue;
}
+ } else if (ws.mIsWallpaper) {
+ // Fall through.
+ } else if (appToken != null) {
+ if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
+ // This app window is of no interest if it is not associated with the
+ // screenshot app.
+ continue;
+ }
+ appWin = ws;
}
- // We keep on including windows until we go past a full-screen
- // window.
- including = !ws.mIsImWindow && !ws.isFullscreen(dw, dh);
+ // Include this window.
final WindowStateAnimator winAnim = ws.mWinAnimator;
if (maxLayer < winAnim.mSurfaceLayer) {
@@ -5771,6 +5861,7 @@
int right = wf.right - cr.right;
int bottom = wf.bottom - cr.bottom;
frame.union(left, top, right, bottom);
+ ws.getStackBounds(stackBounds);
frame.intersect(stackBounds);
}
@@ -5786,60 +5877,49 @@
"Screenshot: Couldn't find a surface matching " + appToken);
return null;
}
+
if (!screenshotReady) {
+ if (retryCount > MAX_SCREENSHOT_RETRIES) {
+ Slog.i(TAG, "Screenshot max retries " + retryCount + " of " + appToken +
+ " appWin=" + (appWin == null ? "null" : (appWin + " drawState=" +
+ appWin.mWinAnimator.mDrawState)));
+ return null;
+ }
+
// Delay and hope that window gets drawn.
if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot: No image ready for " + appToken
+ ", " + appWin + " drawState=" + appWin.mWinAnimator.mDrawState);
continue;
}
- // Constrain frame to the screen size.
- frame.intersect(0, 0, dw, dh);
+ // Screenshot is ready to be taken. Everything from here below will continue
+ // through the bottom of the loop and return a value. We only stay in the loop
+ // because we don't want to release the mWindowMap lock until the screenshot is
+ // taken.
- if (frame.isEmpty() || maxLayer == 0) {
+ if (maxLayer == 0) {
if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken
- + ": returning null frame=" + frame.toShortString() + " maxLayer="
- + maxLayer);
+ + ": returning null maxLayer=" + maxLayer);
return null;
}
- // The screenshot API does not apply the current screen rotation.
- rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
- int fw = frame.width();
- int fh = frame.height();
-
+ // Constrain frame to the screen size.
+ frame.intersect(0, 0, dw, dh);
// Constrain thumbnail to smaller of screen width or height. Assumes aspect
// of thumbnail is the same as the screen (in landscape) or square.
- scale = Math.max(width / (float) fw, height / (float) fh);
- /*
- float targetWidthScale = width / (float) fw;
- float targetHeightScale = height / (float) fh;
- if (fw <= fh) {
- scale = targetWidthScale;
- // If aspect of thumbnail is the same as the screen (in landscape),
- // select the slightly larger value so we fill the entire bitmap
- if (targetHeightScale > scale && (int) (targetHeightScale * fw) == width) {
- scale = targetHeightScale;
- }
- } else {
- scale = targetHeightScale;
- // If aspect of thumbnail is the same as the screen (in landscape),
- // select the slightly larger value so we fill the entire bitmap
- if (targetWidthScale > scale && (int) (targetWidthScale * fh) == height) {
- scale = targetWidthScale;
- }
- }
- */
+ scale = Math.max(width / (float) frame.width(), height / (float) frame.height());
+ dw = (int)(dw * scale);
+ dh = (int)(dh * scale);
- // The screen shot will contain the entire screen.
- dw = (int)(dw*scale);
- dh = (int)(dh*scale);
+ // The screenshot API does not apply the current screen rotation.
+ rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
- int tmp = dw;
+ final int tmp = dw;
dw = dh;
dh = tmp;
rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
}
+
if (DEBUG_SCREENSHOT) {
Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
+ maxLayer + " appToken=" + appToken);
@@ -5850,27 +5930,31 @@
+ " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer);
}
}
- // TODO: Replace 'false' in the following line with a variable that indicates
- // whether the screenshot should use the identity transformation matrix
- // (e.g., enable it when taking a screenshot for recents, since we might be in
- // the middle of the rotation animation, but don't want a rotated recent image).
- // TODO: Replace 'new Rect()' with the portion of the screen to capture for the
- // screenshot.
- rawss = SurfaceControl.screenshot(new Rect(), dw, dh, minLayer, maxLayer, false);
- }
- } while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES);
- if (retryCount > MAX_SCREENSHOT_RETRIES) Slog.i(TAG, "Screenshot max retries " +
- retryCount + " of " + appToken + " appWin=" + (appWin == null ?
- "null" : (appWin + " drawState=" + appWin.mWinAnimator.mDrawState)));
- if (rawss == null) {
- Slog.w(TAG, "Screenshot failure taking screenshot for (" + dw + "x" + dh
- + ") to layer " + maxLayer);
- return null;
+ ScreenRotationAnimation screenRotationAnimation =
+ mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
+ final boolean inRotation = screenRotationAnimation != null &&
+ screenRotationAnimation.isAnimating();
+ if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG,
+ "Taking screenshot while rotating");
+
+ rawss = SurfaceControl.screenshot(new Rect(), dw, dh, minLayer, maxLayer,
+ inRotation);
+ if (rawss == null) {
+ Slog.w(TAG, "Screenshot failure taking screenshot for (" + dw + "x" + dh
+ + ") to layer " + maxLayer);
+ return null;
+ }
+ }
+
+ break;
}
- Bitmap bm = Bitmap.createBitmap(width, height, force565 ? Config.RGB_565 : rawss.getConfig());
- bm.eraseColor(0xFF000000);
+ Bitmap bm = Bitmap.createBitmap(width, height, force565 ?
+ Config.RGB_565 : rawss.getConfig());
+ if (DEBUG_SCREENSHOT) {
+ bm.eraseColor(0xFF000000);
+ }
frame.scale(scale);
Matrix matrix = new Matrix();
ScreenRotationAnimation.createRotationMatrix(rot, dw, dh, matrix);
@@ -7207,6 +7291,10 @@
public static final int NEW_ANIMATOR_SCALE = 34;
+ public static final int UPDATE_SCRN_CAP = 35;
+
+ public static final int SHOW_DISPLAY_MASK = 36;
+
@Override
public void handleMessage(Message msg) {
if (DEBUG_WINDOW_TRACE) {
@@ -7601,6 +7689,11 @@
break;
}
+ case SHOW_DISPLAY_MASK: {
+ showCircularMask();
+ break;
+ }
+
case DO_ANIMATION_CALLBACK: {
try {
((IRemoteCallback)msg.obj).sendResult(null);
@@ -7681,6 +7774,13 @@
}
}
break;
+
+ case UPDATE_SCRN_CAP: {
+ synchronized (mWindowMap) {
+ updateScreenCaptureDisabledLocked(msg.arg1);
+ }
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "handleMessage: exit");
@@ -9116,6 +9216,9 @@
if (mStrictModeFlash != null) {
mStrictModeFlash.positionSurface(defaultDw, defaultDh);
}
+ if (mCircularDisplayMask != null) {
+ mCircularDisplayMask.positionSurface(defaultDw, defaultDh, mRotation);
+ }
boolean focusDisplayed = false;
@@ -11173,17 +11276,14 @@
public void waitForAllWindowsDrawn(Runnable callback, long timeout) {
synchronized (mWindowMap) {
mWaitingForDrawnCallback = callback;
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final WindowList windows =
- mDisplayContents.valueAt(displayNdx).getWindowList();
- for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
- final WindowState win = windows.get(winNdx);
- if (win.mHasSurface && win.isWinVisibleLw() && !win.mIsWallpaper) {
- win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
- // Force add to mResizingWindows.
- win.mLastContentInsets.set(-1, -1, -1, -1);
- mWaitingForDrawn.add(win);
- }
+ final WindowList windows = getDefaultWindowListLocked();
+ for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+ final WindowState win = windows.get(winNdx);
+ if (win.mHasSurface) {
+ win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+ // Force add to mResizingWindows.
+ win.mLastContentInsets.set(-1, -1, -1, -1);
+ mWaitingForDrawn.add(win);
}
}
requestTraversalLocked();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 49d4ae9..3f07dd90 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -39,6 +39,7 @@
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Debug;
+import android.os.UserHandle;
import android.util.Slog;
import android.view.Display;
import android.view.DisplayInfo;
@@ -690,6 +691,10 @@
flags |= SurfaceControl.SECURE;
}
+ if (mService.isScreenCaptureDisabledLocked(UserHandle.getUserId(mWin.mOwnerUid))) {
+ flags |= SurfaceControl.SECURE;
+ }
+
int width;
int height;
if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) {
@@ -735,6 +740,7 @@
mSurfaceX = 0;
mSurfaceY = 0;
w.mLastSystemDecorRect.set(0, 0, 0, 0);
+ mLastClipRect.set(0, 0, 0, 0);
// Set up surface control with initial size.
try {
diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
index 87626d0..46327d7 100644
--- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -53,6 +53,7 @@
static jmethodID method_reportGeofencePauseStatus;
static jmethodID method_reportGeofenceResumeStatus;
static jmethodID method_reportMeasurementData;
+static jmethodID method_reportNavigationMessages;
static const GpsInterface* sGpsInterface = NULL;
static const GpsXtraInterface* sGpsXtraInterface = NULL;
@@ -62,6 +63,7 @@
static const AGpsRilInterface* sAGpsRilInterface = NULL;
static const GpsGeofencingInterface* sGpsGeofencingInterface = NULL;
static const GpsMeasurementInterface* sGpsMeasurementInterface = NULL;
+static const GpsNavigationMessageInterface* sGpsNavigationMessageInterface = NULL;
// temporary storage for GPS callbacks
static GpsSvStatus sGpsSvStatus;
@@ -447,6 +449,10 @@
clazz,
"reportMeasurementData",
"(Landroid/location/GpsMeasurementsEvent;)V");
+ method_reportNavigationMessages = env->GetMethodID(
+ clazz,
+ "reportNavigationMessage",
+ "(Landroid/location/GpsNavigationMessageEvent;)V");
err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
@@ -472,6 +478,9 @@
(const GpsGeofencingInterface*)sGpsInterface->get_extension(GPS_GEOFENCING_INTERFACE);
sGpsMeasurementInterface =
(const GpsMeasurementInterface*)sGpsInterface->get_extension(GPS_MEASUREMENT_INTERFACE);
+ sGpsNavigationMessageInterface =
+ (const GpsNavigationMessageInterface*)sGpsInterface->get_extension(
+ GPS_NAVIGATION_MESSAGE_INTERFACE);
}
}
@@ -861,6 +870,7 @@
static jobject translate_gps_clock(JNIEnv* env, GpsClock* clock) {
const char* doubleSignature = "(D)V";
+ const char* longSignature = "(J)V";
jclass gpsClockClass = env->FindClass("android/location/GpsClock");
jmethodID gpsClockCtor = env->GetMethodID(gpsClockClass, "<init>", "()V");
@@ -873,53 +883,51 @@
env->CallObjectMethod(gpsClockObject, setterMethod, clock->leap_second);
}
- jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setTimeInNs", "(J)V");
+ jmethodID typeSetterMethod = env->GetMethodID(gpsClockClass, "setType", "(B)V");
+ env->CallObjectMethod(gpsClockObject, typeSetterMethod, clock->type);
+
+ jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setTimeInNs", longSignature);
env->CallObjectMethod(gpsClockObject, setterMethod, clock->time_ns);
if (flags & GPS_CLOCK_HAS_TIME_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gpsClockClass,
- "setTimeUncertaintyInNs",
- doubleSignature);
+ jmethodID setterMethod =
+ env->GetMethodID(gpsClockClass, "setTimeUncertaintyInNs", doubleSignature);
env->CallObjectMethod(gpsClockObject, setterMethod, clock->time_uncertainty_ns);
}
+ if (flags & GPS_CLOCK_HAS_FULL_BIAS) {
+ jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setFullBiasInNs", longSignature);
+ env->CallObjectMethod(gpsClockObject, setterMethod, clock->full_bias_ns);
+ }
+
if (flags & GPS_CLOCK_HAS_BIAS) {
jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setBiasInNs", doubleSignature);
env->CallObjectMethod(gpsClockObject, setterMethod, clock->bias_ns);
}
if (flags & GPS_CLOCK_HAS_BIAS_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gpsClockClass,
- "setBiasUncertaintyInNs",
- doubleSignature);
+ jmethodID setterMethod =
+ env->GetMethodID(gpsClockClass, "setBiasUncertaintyInNs", doubleSignature);
env->CallObjectMethod(gpsClockObject, setterMethod, clock->bias_uncertainty_ns);
}
if (flags & GPS_CLOCK_HAS_DRIFT) {
- jmethodID setterMethod = env->GetMethodID(
- gpsClockClass,
- "setDriftInNsPerSec",
- doubleSignature);
+ jmethodID setterMethod =
+ env->GetMethodID(gpsClockClass, "setDriftInNsPerSec", doubleSignature);
env->CallObjectMethod(gpsClockObject, setterMethod, clock->drift_nsps);
}
if (flags & GPS_CLOCK_HAS_DRIFT_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gpsClockClass,
- "setDriftUncertaintyInNsPerSec",
- doubleSignature);
+ jmethodID setterMethod =
+ env->GetMethodID(gpsClockClass, "setDriftUncertaintyInNsPerSec", doubleSignature);
env->CallObjectMethod(gpsClockObject, setterMethod, clock->drift_uncertainty_nsps);
}
return gpsClockObject;
}
-static jobject translate_gps_measurement(
- JNIEnv* env,
- GpsMeasurement* measurement,
- uint32_t time_ns) {
+static jobject translate_gps_measurement(JNIEnv* env, GpsMeasurement* measurement) {
+ const char* byteSignature = "(B)V";
const char* shortSignature = "(S)V";
const char* longSignature = "(J)V";
const char* floatSignature = "(F)V";
@@ -931,16 +939,18 @@
jobject gpsMeasurementObject = env->NewObject(gpsMeasurementClass, gpsMeasurementCtor);
GpsMeasurementFlags flags = measurement->flags;
-
- jmethodID prnSetterMethod = env->GetMethodID(gpsMeasurementClass, "setPrn", "(B)V");
+ jmethodID prnSetterMethod = env->GetMethodID(gpsMeasurementClass, "setPrn", byteSignature);
env->CallObjectMethod(gpsMeasurementObject, prnSetterMethod, measurement->prn);
- jmethodID localTimeSetterMethod =
- env->GetMethodID(gpsMeasurementClass, "setLocalTimeInNs", longSignature);
+ jmethodID timeOffsetSetterMethod =
+ env->GetMethodID(gpsMeasurementClass, "setTimeOffsetInNs", doubleSignature);
env->CallObjectMethod(
gpsMeasurementObject,
- localTimeSetterMethod,
- time_ns + measurement->time_offset_ns);
+ timeOffsetSetterMethod,
+ measurement->time_offset_ns);
+
+ jmethodID stateSetterMethod = env->GetMethodID(gpsMeasurementClass, "setState", shortSignature);
+ env->CallObjectMethod(gpsMeasurementObject, stateSetterMethod, measurement->state);
jmethodID receivedGpsTowSetterMethod =
env->GetMethodID(gpsMeasurementClass, "setReceivedGpsTowInNs", longSignature);
@@ -949,10 +959,17 @@
receivedGpsTowSetterMethod,
measurement->received_gps_tow_ns);
- jmethodID cn0SetterMethod = env->GetMethodID(
+ jmethodID receivedGpsTowUncertaintySetterMethod = env->GetMethodID(
gpsMeasurementClass,
- "setCn0InDbHz",
- doubleSignature);
+ "setReceivedGpsTowUncertaintyInNs",
+ longSignature);
+ env->CallVoidMethod(
+ gpsMeasurementObject,
+ receivedGpsTowUncertaintySetterMethod,
+ measurement->received_gps_tow_uncertainty_ns);
+
+ jmethodID cn0SetterMethod =
+ env->GetMethodID(gpsMeasurementClass, "setCn0InDbHz", doubleSignature);
env->CallObjectMethod(gpsMeasurementObject, cn0SetterMethod, measurement->c_n0_dbhz);
jmethodID pseudorangeRateSetterMethod = env->GetMethodID(
@@ -962,7 +979,7 @@
env->CallObjectMethod(
gpsMeasurementObject,
pseudorangeRateSetterMethod,
- measurement->pseudorange_rate_mpersec);
+ measurement->pseudorange_rate_mps);
jmethodID pseudorangeRateUncertaintySetterMethod = env->GetMethodID(
gpsMeasurementClass,
@@ -971,7 +988,14 @@
env->CallObjectMethod(
gpsMeasurementObject,
pseudorangeRateUncertaintySetterMethod,
- measurement->pseudorange_rate_uncertainty_mpersec);
+ measurement->pseudorange_rate_uncertainty_mps);
+
+ jmethodID accumulatedDeltaRangeStateSetterMethod =
+ env->GetMethodID(gpsMeasurementClass, "setAccumulatedDeltaRangeState", shortSignature);
+ env->CallObjectMethod(
+ gpsMeasurementObject,
+ accumulatedDeltaRangeStateSetterMethod,
+ measurement->accumulated_delta_range_state);
jmethodID accumulatedDeltaRangeSetterMethod = env->GetMethodID(
gpsMeasurementClass,
@@ -991,12 +1015,9 @@
accumulatedDeltaRangeUncertaintySetterMethod,
measurement->accumulated_delta_range_uncertainty_m);
-
if (flags & GPS_MEASUREMENT_HAS_PSEUDORANGE) {
- jmethodID setterMethod = env->GetMethodID(
- gpsMeasurementClass,
- "setPseudorangeInMeters",
- doubleSignature);
+ jmethodID setterMethod =
+ env->GetMethodID(gpsMeasurementClass, "setPseudorangeInMeters", doubleSignature);
env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->pseudorange_m);
}
@@ -1012,10 +1033,8 @@
}
if (flags & GPS_MEASUREMENT_HAS_CODE_PHASE) {
- jmethodID setterMethod = env->GetMethodID(
- gpsMeasurementClass,
- "setCodePhaseInChips",
- doubleSignature);
+ jmethodID setterMethod =
+ env->GetMethodID(gpsMeasurementClass, "setCodePhaseInChips", doubleSignature);
env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->code_phase_chips);
}
@@ -1031,10 +1050,8 @@
}
if (flags & GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY) {
- jmethodID setterMethod = env->GetMethodID(
- gpsMeasurementClass,
- "setCarrierFrequencyInHz",
- floatSignature);
+ jmethodID setterMethod =
+ env->GetMethodID(gpsMeasurementClass, "setCarrierFrequencyInHz", floatSignature);
env->CallObjectMethod(
gpsMeasurementObject,
setterMethod,
@@ -1065,33 +1082,27 @@
}
jmethodID lossOfLockSetterMethod =
- env->GetMethodID(gpsMeasurementClass, "setLossOfLock", shortSignature);
+ env->GetMethodID(gpsMeasurementClass, "setLossOfLock", byteSignature);
env->CallObjectMethod(gpsMeasurementObject, lossOfLockSetterMethod, measurement->loss_of_lock);
if (flags & GPS_MEASUREMENT_HAS_BIT_NUMBER) {
- jmethodID setterMethod = env->GetMethodID(
- gpsMeasurementClass,
- "setBitNumber",
- shortSignature);
+ jmethodID setterMethod =
+ env->GetMethodID(gpsMeasurementClass, "setBitNumber", shortSignature);
env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->bit_number);
}
if (flags & GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT) {
- jmethodID setterMethod = env->GetMethodID(
- gpsMeasurementClass,
- "setTimeFromLastBitInNs",
- longSignature);
+ jmethodID setterMethod =
+ env->GetMethodID(gpsMeasurementClass, "setTimeFromLastBitInMs", shortSignature);
env->CallObjectMethod(
gpsMeasurementObject,
setterMethod,
- measurement->time_from_last_bit_ns);
+ measurement->time_from_last_bit_ms);
}
if (flags & GPS_MEASUREMENT_HAS_DOPPLER_SHIFT) {
- jmethodID setterMethod = env->GetMethodID(
- gpsMeasurementClass,
- "setDopplerShiftInHz",
- doubleSignature);
+ jmethodID setterMethod =
+ env->GetMethodID(gpsMeasurementClass, "setDopplerShiftInHz", doubleSignature);
env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->doppler_shift_hz);
}
@@ -1106,10 +1117,8 @@
measurement->doppler_shift_uncertainty_hz);
}
- jmethodID multipathIndicatorSetterMethod = env->GetMethodID(
- gpsMeasurementClass,
- "setMultipathIndicator",
- shortSignature);
+ jmethodID multipathIndicatorSetterMethod =
+ env->GetMethodID(gpsMeasurementClass, "setMultipathIndicator", byteSignature);
env->CallObjectMethod(
gpsMeasurementObject,
multipathIndicatorSetterMethod,
@@ -1122,18 +1131,14 @@
}
if (flags & GPS_MEASUREMENT_HAS_ELEVATION) {
- jmethodID setterMethod = env->GetMethodID(
- gpsMeasurementClass,
- "setElevationInDeg",
- doubleSignature);
+ jmethodID setterMethod =
+ env->GetMethodID(gpsMeasurementClass, "setElevationInDeg", doubleSignature);
env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->elevation_deg);
}
if (flags & GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gpsMeasurementClass,
- "setElevationUncertaintyInDeg",
- doubleSignature);
+ jmethodID setterMethod =
+ env->GetMethodID(gpsMeasurementClass, "setElevationUncertaintyInDeg", doubleSignature);
env->CallObjectMethod(
gpsMeasurementObject,
setterMethod,
@@ -1180,10 +1185,7 @@
GpsMeasurement* gpsMeasurements = data->measurements;
for (uint16_t i = 0; i < measurementCount; ++i) {
- jobject gpsMeasurement = translate_gps_measurement(
- env,
- &gpsMeasurements[i],
- data->clock.time_ns);
+ jobject gpsMeasurement = translate_gps_measurement(env, &gpsMeasurements[i]);
env->SetObjectArrayElement(gpsMeasurementArray, i, gpsMeasurement);
env->DeleteLocalRef(gpsMeasurement);
}
@@ -1219,7 +1221,6 @@
checkAndClearExceptionFromCallback(env, __FUNCTION__);
} else {
ALOGE("Invalid GpsData size found in gps_measurement_callback, size=%d", data->size);
- return;
}
}
@@ -1230,7 +1231,7 @@
static jboolean android_location_GpsLocationProvider_is_measurement_supported(
JNIEnv* env,
- jobject obj) {
+ jclass clazz) {
if (sGpsMeasurementInterface != NULL) {
return JNI_TRUE;
}
@@ -1266,6 +1267,110 @@
return JNI_TRUE;
}
+static jobject translate_gps_navigation_message(JNIEnv* env, GpsNavigationMessage* message) {
+ size_t dataLength = message->data_length;
+ uint8_t* data = message->data;
+ if (dataLength == 0 || data == NULL) {
+ ALOGE("Invalid Navigation Message found: data=%p, length=%d", data, dataLength);
+ return NULL;
+ }
+
+ jclass navigationMessageClass = env->FindClass("android/location/GpsNavigationMessage");
+ jmethodID navigationMessageCtor = env->GetMethodID(navigationMessageClass, "<init>", "()V");
+ jobject navigationMessageObject = env->NewObject(navigationMessageClass, navigationMessageCtor);
+
+ jmethodID setTypeMethod = env->GetMethodID(navigationMessageClass, "setType", "(B)V");
+ env->CallVoidMethod(navigationMessageObject, setTypeMethod, message->type);
+
+ jmethodID setPrnMethod = env->GetMethodID(navigationMessageClass, "setPrn", "(B)V");
+ env->CallVoidMethod(navigationMessageObject, setPrnMethod, message->prn);
+
+ jmethodID setMessageIdMethod = env->GetMethodID(navigationMessageClass, "setMessageId", "(S)V");
+ env->CallVoidMethod(navigationMessageObject, setMessageIdMethod, message->message_id);
+
+ jmethodID setSubmessageIdMethod =
+ env->GetMethodID(navigationMessageClass, "setSubmessageId", "(S)V");
+ env->CallVoidMethod(navigationMessageObject, setSubmessageIdMethod, message->submessage_id);
+
+ jbyteArray dataArray = env->NewByteArray(dataLength);
+ env->SetByteArrayRegion(dataArray, 0, dataLength, (jbyte*) data);
+ jmethodID setDataMethod = env->GetMethodID(navigationMessageClass, "setData", "([B)V");
+ env->CallVoidMethod(navigationMessageObject, setDataMethod, dataArray);
+
+ return navigationMessageObject;
+}
+
+static void navigation_message_callback(GpsNavigationMessage* message) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (message == NULL) {
+ ALOGE("Invalid Navigation Message provided to callback");
+ return;
+ }
+
+ if (message->size == sizeof(GpsNavigationMessage)) {
+ jobject navigationMessage = translate_gps_navigation_message(env, message);
+
+ jclass navigationMessageEventClass =
+ env->FindClass("android/location/GpsNavigationMessageEvent");
+ jmethodID navigationMessageEventCtor = env->GetMethodID(
+ navigationMessageEventClass,
+ "<init>",
+ "(Landroid/location/GpsNavigationMessage;)V");
+ jobject navigationMessageEvent = env->NewObject(
+ navigationMessageEventClass,
+ navigationMessageEventCtor,
+ navigationMessage);
+
+ env->CallVoidMethod(mCallbacksObj, method_reportNavigationMessages, navigationMessageEvent);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ } else {
+ ALOGE("Invalid GpsNavigationMessage size found: %d", message->size);
+ }
+}
+
+GpsNavigationMessageCallbacks sGpsNavigationMessageCallbacks = {
+ sizeof(GpsNavigationMessageCallbacks),
+ navigation_message_callback,
+};
+
+static jboolean android_location_GpsLocationProvider_is_navigation_message_supported(
+ JNIEnv* env,
+ jclass clazz) {
+ if(sGpsNavigationMessageInterface != NULL) {
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_start_navigation_message_collection(
+ JNIEnv* env,
+ jobject obj) {
+ if (sGpsNavigationMessageInterface == NULL) {
+ ALOGE("Navigation Message interface is not available.");
+ return JNI_FALSE;
+ }
+
+ int result = sGpsNavigationMessageInterface->init(&sGpsNavigationMessageCallbacks);
+ if (result != GPS_NAVIGATION_MESSAGE_OPERATION_SUCCESS) {
+ ALOGE("An error has been found in %s: %d", __FUNCTION__, result);
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+static jboolean android_location_GpsLocationProvider_stop_navigation_message_collection(
+ JNIEnv* env,
+ jobject obj) {
+ if (sGpsNavigationMessageInterface == NULL) {
+ ALOGE("Navigation Message interface is not available.");
+ return JNI_FALSE;
+ }
+
+ sGpsNavigationMessageInterface->close();
+ return JNI_TRUE;
+}
+
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
@@ -1343,7 +1448,16 @@
(void*) android_location_GpsLocationProvider_start_measurement_collection},
{"native_stop_measurement_collection",
"()Z",
- (void*) android_location_GpsLocationProvider_stop_measurement_collection}
+ (void*) android_location_GpsLocationProvider_stop_measurement_collection},
+ {"native_is_navigation_message_supported",
+ "()Z",
+ (void*) android_location_GpsLocationProvider_is_navigation_message_supported},
+ {"native_start_navigation_message_collection",
+ "()Z",
+ (void*) android_location_GpsLocationProvider_start_navigation_message_collection},
+ {"native_stop_navigation_message_collection",
+ "()Z",
+ (void*) android_location_GpsLocationProvider_stop_navigation_message_collection},
};
int register_android_server_location_GpsLocationProvider(JNIEnv* env)
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index a9d5c72..41976ff 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -36,6 +36,7 @@
jmethodID deviceAvailable;
jmethodID deviceUnavailable;
jmethodID streamConfigsChanged;
+ jmethodID firstFrameCaptured;
} gTvInputHalClassInfo;
static struct {
@@ -539,6 +540,14 @@
thread = connection.mThread;
}
thread->onCaptured(seq, succeeded);
+ if (seq == 0) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(
+ mThiz,
+ gTvInputHalClassInfo.firstFrameCaptured,
+ deviceId,
+ streamId);
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -638,6 +647,9 @@
GET_METHOD_ID(
gTvInputHalClassInfo.streamConfigsChanged, clazz,
"streamConfigsChangedFromNative", "(I)V");
+ GET_METHOD_ID(
+ gTvInputHalClassInfo.firstFrameCaptured, clazz,
+ "firstFrameCapturedFromNative", "(II)V");
FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/media/tv/TvStreamConfig");
gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 12ed920..e0612eb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -258,6 +258,7 @@
private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
private static final String TAG_DISABLE_CAMERA = "disable-camera";
private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
+ private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture";
private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
private static final String TAG_ACCOUNT_TYPE = "account-type";
private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
@@ -326,6 +327,8 @@
boolean encryptionRequested = false;
boolean disableCamera = false;
boolean disableCallerId = false;
+ boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
+
Set<String> accountTypesWithManagementDisabled = new HashSet<String>();
// TODO: review implementation decisions with frameworks team
@@ -443,6 +446,11 @@
out.attribute(null, ATTR_VALUE, Boolean.toString(disableCallerId));
out.endTag(null, TAG_DISABLE_CALLER_ID);
}
+ if (disableScreenCapture) {
+ out.startTag(null, TAG_DISABLE_SCREEN_CAPTURE);
+ out.attribute(null, ATTR_VALUE, Boolean.toString(disableScreenCapture));
+ out.endTag(null, TAG_DISABLE_SCREEN_CAPTURE);
+ }
if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
out.startTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
@@ -528,6 +536,9 @@
} else if (TAG_DISABLE_CALLER_ID.equals(tag)) {
disableCallerId = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_DISABLE_SCREEN_CAPTURE.equals(tag)) {
+ disableScreenCapture = Boolean.parseBoolean(
+ parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
disabledKeyguardFeatures = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
@@ -606,6 +617,8 @@
pw.println(disableCamera);
pw.print(prefix); pw.print("disableCallerId=");
pw.println(disableCallerId);
+ pw.print(prefix); pw.print("disableScreenCapture=");
+ pw.println(disableScreenCapture);
pw.print(prefix); pw.print("disabledKeyguardFeatures=");
pw.println(disabledKeyguardFeatures);
}
@@ -1103,6 +1116,8 @@
type = parser.next();
int outerDepth = parser.getDepth();
policy.mLockTaskPackages.clear();
+ policy.mAdminList.clear();
+ policy.mAdminMap.clear();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -1124,7 +1139,6 @@
ActiveAdmin ap = new ActiveAdmin(dai);
ap.readFromXml(parser);
policy.mAdminMap.put(ap.info.getComponent(), ap);
- policy.mAdminList.add(ap);
}
} catch (RuntimeException e) {
Slog.w(LOG_TAG, "Failed loading admin " + name, e);
@@ -1184,6 +1198,9 @@
// Ignore
}
+ // Generate a list of admins from the admin map
+ policy.mAdminList.addAll(policy.mAdminMap.values());
+
// Validate that what we stored for the password quality matches
// sufficiently what is currently set. Note that this is only
// a sanity check in case the two get out of sync; this should
@@ -1268,10 +1285,8 @@
if (!mHasFeature) {
return;
}
- synchronized (this) {
- loadSettingsLocked(getUserData(UserHandle.USER_OWNER), UserHandle.USER_OWNER);
- loadDeviceOwner();
- }
+ getUserData(UserHandle.USER_OWNER);
+ loadDeviceOwner();
cleanUpOldUsers();
mAppOpsService = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
@@ -1436,14 +1451,12 @@
ActiveAdmin newAdmin = new ActiveAdmin(info);
policy.mAdminMap.put(adminReceiver, newAdmin);
int replaceIndex = -1;
- if (refreshing) {
- final int N = policy.mAdminList.size();
- for (int i=0; i < N; i++) {
- ActiveAdmin oldAdmin = policy.mAdminList.get(i);
- if (oldAdmin.info.getComponent().equals(adminReceiver)) {
- replaceIndex = i;
- break;
- }
+ final int N = policy.mAdminList.size();
+ for (int i=0; i < N; i++) {
+ ActiveAdmin oldAdmin = policy.mAdminList.get(i);
+ if (oldAdmin.info.getComponent().equals(adminReceiver)) {
+ replaceIndex = i;
+ break;
}
}
if (replaceIndex == -1) {
@@ -2575,6 +2588,7 @@
return;
}
Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED);
+ intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.setComponent(admin.info.getComponent());
mContext.sendOrderedBroadcastAsUser(intent, new UserHandle(userHandle),
null, new BroadcastReceiver() {
@@ -2976,6 +2990,58 @@
private void setEncryptionRequested(boolean encrypt) {
}
+
+ /**
+ * Set whether the screen capture is disabled for the user managed by the specified admin.
+ */
+ public void setScreenCaptureDisabled(ComponentName who, int userHandle, boolean disabled) {
+ if (!mHasFeature) {
+ return;
+ }
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (ap.disableScreenCapture != disabled) {
+ ap.disableScreenCapture = disabled;
+ saveSettingsLocked(userHandle);
+ try {
+ getWindowManager().updateScreenCaptureDisabled(userHandle);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns whether or not screen capture is disabled for a given admin, or disabled for any
+ * active admin (if given admin is null).
+ */
+ public boolean getScreenCaptureDisabled(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+ synchronized (this) {
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return (admin != null) ? admin.disableScreenCapture : false;
+ }
+
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (admin.disableScreenCapture) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
/**
* The system property used to share the state of the camera. The native camera service
* is expected to read this property and act accordingly.
@@ -3708,8 +3774,8 @@
}
@Override
- public boolean setApplicationBlocked(ComponentName who, String packageName,
- boolean blocked) {
+ public boolean setApplicationHidden(ComponentName who, String packageName,
+ boolean hidden) {
int callingUserId = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
@@ -3720,10 +3786,10 @@
long id = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
- return pm.setApplicationBlockedSettingAsUser(packageName, blocked, callingUserId);
+ return pm.setApplicationHiddenSettingAsUser(packageName, hidden, callingUserId);
} catch (RemoteException re) {
// shouldn't happen
- Slog.e(LOG_TAG, "Failed to setApplicationBlockedSetting", re);
+ Slog.e(LOG_TAG, "Failed to setApplicationHiddenSetting", re);
} finally {
restoreCallingIdentity(id);
}
@@ -3732,7 +3798,7 @@
}
@Override
- public int setApplicationsBlocked(ComponentName who, Intent intent, boolean blocked) {
+ public int setApplicationsHidden(ComponentName who, Intent intent, boolean hidden) {
int callingUserId = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
@@ -3750,20 +3816,20 @@
callingUserId);
if (DBG) Slog.d(LOG_TAG, "Enabling activities: " + activitiesToEnable);
- int numberOfAppsUnblocked = 0;
+ int numberOfAppsUnhidden = 0;
if (activitiesToEnable != null) {
for (ResolveInfo info : activitiesToEnable) {
if (info.activityInfo != null) {
- numberOfAppsUnblocked++;
- pm.setApplicationBlockedSettingAsUser(info.activityInfo.packageName,
- blocked, callingUserId);
+ numberOfAppsUnhidden++;
+ pm.setApplicationHiddenSettingAsUser(info.activityInfo.packageName,
+ hidden, callingUserId);
}
}
}
- return numberOfAppsUnblocked;
+ return numberOfAppsUnhidden;
} catch (RemoteException re) {
// shouldn't happen
- Slog.e(LOG_TAG, "Failed to setApplicationsBlockedSettingsWithIntent", re);
+ Slog.e(LOG_TAG, "Failed to setApplicationsHiddenSettingsWithIntent", re);
} finally {
restoreCallingIdentity(id);
}
@@ -3772,7 +3838,7 @@
}
@Override
- public boolean isApplicationBlocked(ComponentName who, String packageName) {
+ public boolean isApplicationHidden(ComponentName who, String packageName) {
int callingUserId = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
@@ -3783,10 +3849,10 @@
long id = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
- return pm.getApplicationBlockedSettingAsUser(packageName, callingUserId);
+ return pm.getApplicationHiddenSettingAsUser(packageName, callingUserId);
} catch (RemoteException re) {
// shouldn't happen
- Slog.e(LOG_TAG, "Failed to getApplicationBlockedSettingAsUser", re);
+ Slog.e(LOG_TAG, "Failed to getApplicationHiddenSettingAsUser", re);
} finally {
restoreCallingIdentity(id);
}
@@ -4030,7 +4096,8 @@
@Override
public boolean getCrossProfileCallerIdDisabledForUser(int userId) {
- enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system");
+ // TODO: Should there be a check to make sure this relationship is within a profile group?
+ //enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system");
synchronized (this) {
ActiveAdmin admin = getProfileOwnerAdmin(userId);
return (admin != null) ? admin.disableCallerId : false;
diff --git a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
index 4fe30e6..fb29b6a 100644
--- a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
+++ b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
@@ -40,6 +40,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.IUserManager;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -103,7 +104,8 @@
@Override
public void requestPermission(final String packageName, final String requestType,
- final Bundle requestData) throws RemoteException {
+ final String requestId,
+ final PersistableBundle requestData) throws RemoteException {
if (DEBUG) {
Log.i(LOG_TAG, "requestPermission");
}
@@ -127,6 +129,7 @@
intent.setComponent(restrictionsProvider);
intent.putExtra(RestrictionsManager.EXTRA_PACKAGE_NAME, packageName);
intent.putExtra(RestrictionsManager.EXTRA_REQUEST_TYPE, requestType);
+ intent.putExtra(RestrictionsManager.EXTRA_REQUEST_ID, requestId);
intent.putExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE, requestData);
mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
} finally {
@@ -136,7 +139,40 @@
}
@Override
- public void notifyPermissionResponse(String packageName, Bundle response)
+ public Intent getLocalApprovalIntent() throws RemoteException {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "requestPermission");
+ }
+ final int userHandle = UserHandle.getCallingUserId();
+ if (mDpm != null) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ ComponentName restrictionsProvider =
+ mDpm.getRestrictionsProvider(userHandle);
+ // Check if there is a restrictions provider
+ if (restrictionsProvider == null) {
+ throw new IllegalStateException(
+ "Cannot request permission without a restrictions provider registered");
+ }
+ String providerPackageName = restrictionsProvider.getPackageName();
+ Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_LOCAL_APPROVAL);
+ intent.setPackage(providerPackageName);
+ ResolveInfo ri = AppGlobals.getPackageManager().resolveIntent(intent,
+ null /* resolvedType */, 0 /* flags */, userHandle);
+ if (ri != null && ri.activityInfo != null && ri.activityInfo.exported) {
+ intent.setComponent(new ComponentName(ri.activityInfo.packageName,
+ ri.activityInfo.name));
+ return intent;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void notifyPermissionResponse(String packageName, PersistableBundle response)
throws RemoteException {
// Check caller
int callingUid = Binder.getCallingUid();
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 88aaafc..9e3dec8 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -162,12 +162,10 @@
mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
nextConnBroadcast.get();
- // verify that both routes were added and DNS was flushed
+ // verify that both routes were added
int mobileNetId = mMobile.tracker.getNetwork().netId;
verify(mNetManager).addRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V4));
verify(mNetManager).addRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V6));
- verify(mNetManager).flushNetworkDnsCache(mobileNetId);
-
}
public void testMobileWifiHandoff() throws Exception {
@@ -204,7 +202,6 @@
int wifiNetId = mWifi.tracker.getNetwork().netId;
verify(mNetManager).addRoute(eq(wifiNetId), eq(WIFI_ROUTE_V4));
verify(mNetManager).addRoute(eq(wifiNetId), eq(WIFI_ROUTE_V6));
- verify(mNetManager).flushNetworkDnsCache(wifiNetId);
verify(mMobile.tracker).teardown();
int mobileNetId = mMobile.tracker.getNetwork().netId;
diff --git a/services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
similarity index 64%
rename from services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java
rename to services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index cb8da70..2b693a3 100644
--- a/services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -1,4 +1,4 @@
-package com.android.server.task;
+package com.android.server.job;
import android.content.ComponentName;
@@ -9,40 +9,32 @@
import android.test.AndroidTestCase;
import android.test.RenamingDelegatingContext;
import android.util.Log;
+import android.util.ArraySet;
-import com.android.server.job.JobMapReadFinishedListener;
-import com.android.server.job.JobStore;
import com.android.server.job.controllers.JobStatus;
-import java.util.List;
+import java.util.Iterator;
/**
* Test reading and writing correctly from file.
*/
-public class TaskStoreTest extends AndroidTestCase {
+public class JobStoreTest extends AndroidTestCase {
private static final String TAG = "TaskStoreTest";
private static final String TEST_PREFIX = "_test_";
- // private static final int USER_NON_0 = 3;
+
private static final int SOME_UID = 34234;
private ComponentName mComponent;
- private static final long IO_WAIT = 600L;
+ private static final long IO_WAIT = 1000L;
JobStore mTaskStoreUnderTest;
Context mTestContext;
- JobMapReadFinishedListener mTaskMapReadFinishedListenerStub =
- new JobMapReadFinishedListener() {
- @Override
- public void onJobMapReadFinished(List<JobStatus> tasks) {
- // do nothing.
- }
- };
@Override
public void setUp() throws Exception {
mTestContext = new RenamingDelegatingContext(getContext(), TEST_PREFIX);
Log.d(TAG, "Saving tasks to '" + mTestContext.getFilesDir() + "'");
- mTaskStoreUnderTest = JobStore.initAndGetForTesting(mTestContext,
- mTestContext.getFilesDir(), mTaskMapReadFinishedListenerStub);
+ mTaskStoreUnderTest =
+ JobStore.initAndGetForTesting(mTestContext, mTestContext.getFilesDir());
mComponent = new ComponentName(getContext().getPackageName(), StubClass.class.getName());
}
@@ -69,19 +61,17 @@
mTaskStoreUnderTest.add(ts);
Thread.sleep(IO_WAIT);
// Manually load tasks from xml file.
- mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() {
- @Override
- public void onJobMapReadFinished(List<JobStatus> tasks) {
- assertEquals("Didn't get expected number of persisted tasks.", 1, tasks.size());
- JobStatus loadedTaskStatus = tasks.get(0);
- assertTasksEqual(task, loadedTaskStatus.getJob());
- assertEquals("Different uids.", SOME_UID, tasks.get(0).getUid());
- compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
- ts.getEarliestRunTime(), loadedTaskStatus.getEarliestRunTime());
- compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
- ts.getLatestRunTimeElapsed(), loadedTaskStatus.getLatestRunTimeElapsed());
- }
- });
+ final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
+
+ assertEquals("Didn't get expected number of persisted tasks.", 1, jobStatusSet.size());
+ final JobStatus loadedTaskStatus = jobStatusSet.iterator().next();
+ assertTasksEqual(task, loadedTaskStatus.getJob());
+ assertEquals("Different uids.", SOME_UID, loadedTaskStatus.getUid());
+ compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
+ ts.getEarliestRunTime(), loadedTaskStatus.getEarliestRunTime());
+ compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
+ ts.getLatestRunTimeElapsed(), loadedTaskStatus.getLatestRunTimeElapsed());
}
@@ -104,26 +94,25 @@
mTaskStoreUnderTest.add(taskStatus1);
mTaskStoreUnderTest.add(taskStatus2);
Thread.sleep(IO_WAIT);
- mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() {
- @Override
- public void onJobMapReadFinished(List<JobStatus> tasks) {
- assertEquals("Incorrect # of persisted tasks.", 2, tasks.size());
- JobStatus loaded1 = tasks.get(0);
- JobStatus loaded2 = tasks.get(1);
- assertTasksEqual(task1, loaded1.getJob());
- assertTasksEqual(task2, loaded2.getJob());
- // Check that the loaded task has the correct runtimes.
- compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
- taskStatus1.getEarliestRunTime(), loaded1.getEarliestRunTime());
- compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
- taskStatus1.getLatestRunTimeElapsed(), loaded1.getLatestRunTimeElapsed());
- compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
- taskStatus2.getEarliestRunTime(), loaded2.getEarliestRunTime());
- compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
- taskStatus2.getLatestRunTimeElapsed(), loaded2.getLatestRunTimeElapsed());
- }
- });
+ final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
+ assertEquals("Incorrect # of persisted tasks.", 2, jobStatusSet.size());
+ Iterator<JobStatus> it = jobStatusSet.iterator();
+ JobStatus loaded1 = it.next();
+ JobStatus loaded2 = it.next();
+ assertTasksEqual(task1, loaded1.getJob());
+ assertTasksEqual(task2, loaded2.getJob());
+
+ // Check that the loaded task has the correct runtimes.
+ compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
+ taskStatus1.getEarliestRunTime(), loaded1.getEarliestRunTime());
+ compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
+ taskStatus1.getLatestRunTimeElapsed(), loaded1.getLatestRunTimeElapsed());
+ compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
+ taskStatus2.getEarliestRunTime(), loaded2.getEarliestRunTime());
+ compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
+ taskStatus2.getLatestRunTimeElapsed(), loaded2.getLatestRunTimeElapsed());
}
@@ -144,15 +133,12 @@
mTaskStoreUnderTest.add(taskStatus);
Thread.sleep(IO_WAIT);
- mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() {
- @Override
- public void onJobMapReadFinished(List<JobStatus> tasks) {
- assertEquals("Incorrect # of persisted tasks.", 1, tasks.size());
- JobStatus loaded = tasks.get(0);
- assertTasksEqual(task, loaded.getJob());
- }
- });
+ final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
+ assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
+ JobStatus loaded = jobStatusSet.iterator().next();
+ assertTasksEqual(task, loaded.getJob());
}
/**
@@ -201,4 +187,4 @@
private static class StubClass {}
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
new file mode 100644
index 0000000..3cc04e8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import android.app.Notification;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.ArrayList;
+
+public class RankingHelperTest extends AndroidTestCase {
+
+ private Notification mNotiGroupGSortA;
+ private Notification mNotiGroupGSortB;
+ private Notification mNotiNoGroup;
+ private Notification mNotiNoGroup2;
+ private Notification mNotiNoGroupSortA;
+ private NotificationRecord mRecordGroupGSortA;
+ private NotificationRecord mRecordGroupGSortB;
+ private NotificationRecord mRecordNoGroup;
+ private NotificationRecord mRecordNoGroup2;
+ private NotificationRecord mRecordNoGroupSortA;
+ private RankingHelper mHelper;
+
+ @Override
+ public void setUp() {
+ UserHandle user = UserHandle.ALL;
+
+ mHelper = new RankingHelper(getContext(), null, new String[0]);
+
+ mNotiGroupGSortA = new Notification.Builder(getContext())
+ .setContentTitle("A")
+ .setGroup("G")
+ .setSortKey("A")
+ .setWhen(1205)
+ .build();
+ mRecordGroupGSortA = new NotificationRecord(new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortA, user), 0);
+
+ mNotiGroupGSortB = new Notification.Builder(getContext())
+ .setContentTitle("B")
+ .setGroup("G")
+ .setSortKey("B")
+ .setWhen(1200)
+ .build();
+ mRecordGroupGSortB = new NotificationRecord(new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortB, user), 0);
+
+ mNotiNoGroup = new Notification.Builder(getContext())
+ .setContentTitle("C")
+ .setWhen(1201)
+ .build();
+ mRecordNoGroup = new NotificationRecord(new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiNoGroup, user), 0);
+
+ mNotiNoGroup2 = new Notification.Builder(getContext())
+ .setContentTitle("D")
+ .setWhen(1202)
+ .build();
+ mRecordNoGroup2 = new NotificationRecord(new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiNoGroup2, user), 0);
+
+ mNotiNoGroupSortA = new Notification.Builder(getContext())
+ .setContentTitle("E")
+ .setWhen(1201)
+ .setSortKey("A")
+ .build();
+ mRecordNoGroupSortA = new NotificationRecord(new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiNoGroupSortA, user), 0);
+ }
+
+ @SmallTest
+ public void testFindAfterRankingWithASplitGroup() throws Exception {
+ ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3);
+ notificationList.add(mRecordGroupGSortA);
+ notificationList.add(mRecordGroupGSortB);
+ notificationList.add(mRecordNoGroup);
+ notificationList.add(mRecordNoGroupSortA);
+ mHelper.sort(notificationList);
+ assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortA) >= 0);
+ assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortB) >= 0);
+ assertTrue(mHelper.indexOf(notificationList, mRecordNoGroup) >= 0);
+ assertTrue(mHelper.indexOf(notificationList, mRecordNoGroupSortA) >= 0);
+ }
+
+ @SmallTest
+ public void testSortShouldNotThrowWithPlainNotifications() throws Exception {
+ ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2);
+ notificationList.add(mRecordNoGroup);
+ notificationList.add(mRecordNoGroup2);
+ mHelper.sort(notificationList);
+ }
+
+ @SmallTest
+ public void testSortShouldNotThrowOneSorted() throws Exception {
+ ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2);
+ notificationList.add(mRecordNoGroup);
+ notificationList.add(mRecordNoGroupSortA);
+ mHelper.sort(notificationList);
+ }
+
+ @SmallTest
+ public void testSortShouldNotThrowOneNotification() throws Exception {
+ ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1);
+ notificationList.add(mRecordNoGroup);
+ mHelper.sort(notificationList);
+ }
+
+ @SmallTest
+ public void testSortShouldNotThrowOneSortKey() throws Exception {
+ ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1);
+ notificationList.add(mRecordGroupGSortB);
+ mHelper.sort(notificationList);
+ }
+
+ @SmallTest
+ public void testSortShouldNotThrowOnEmptyList() throws Exception {
+ ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
+ mHelper.sort(notificationList);
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 13cbf8a..4018def 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -16,6 +16,7 @@
package com.android.server.usage;
+import android.Manifest;
import android.app.AppOpsManager;
import android.app.usage.IUsageStatsManager;
import android.app.usage.PackageUsageStats;
@@ -25,6 +26,7 @@
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -381,10 +383,21 @@
private class BinderService extends IUsageStatsManager.Stub {
+ private boolean hasPermission(String callingPackage) {
+ final int mode = mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS,
+ Binder.getCallingUid(), callingPackage);
+ if (mode == AppOpsManager.MODE_DEFAULT) {
+ // The default behavior here is to check if PackageManager has given the app
+ // permission.
+ return getContext().checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+ return mode == AppOpsManager.MODE_ALLOWED;
+ }
+
@Override
public UsageStats[] getStatsSince(int bucketType, long time, String callingPackage) {
- if (mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ if (!hasPermission(callingPackage)) {
return UsageStats.EMPTY_STATS;
}
@@ -398,8 +411,7 @@
@Override
public UsageStats.Event[] getEventsSince(long time, String callingPackage) {
- if (mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ if (!hasPermission(callingPackage)) {
return UsageStats.Event.EMPTY_EVENTS;
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index 50be1dc..1e0d6de 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -100,29 +100,32 @@
}
public boolean addOrUpdateKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
- SQLiteDatabase db = getWritableDatabase();
- ContentValues values = new ContentValues();
- // Generate a random ID for the model.
- values.put(SoundModelContract.KEY_ID, soundModel.uuid.toString());
- values.put(SoundModelContract.KEY_DATA, soundModel.data);
- values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
-
- boolean status = true;
- if (db.insertWithOnConflict(
- SoundModelContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
- for (Keyphrase keyphrase : soundModel.keyphrases) {
- status &= addOrUpdateKeyphrase(db, soundModel.uuid, keyphrase);
+ synchronized(this) {
+ SQLiteDatabase db = getWritableDatabase();
+ ContentValues values = new ContentValues();
+ // Generate a random ID for the model.
+ values.put(SoundModelContract.KEY_ID, soundModel.uuid.toString());
+ values.put(SoundModelContract.KEY_DATA, soundModel.data);
+ values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
+
+ boolean status = true;
+ if (db.insertWithOnConflict(SoundModelContract.TABLE, null, values,
+ SQLiteDatabase.CONFLICT_REPLACE) != -1) {
+ for (Keyphrase keyphrase : soundModel.keyphrases) {
+ status &= addOrUpdateKeyphraseLocked(db, soundModel.uuid, keyphrase);
+ }
+ db.close();
+ return status;
+ } else {
+ Slog.w(TAG, "Failed to persist sound model to database");
+ db.close();
+ return false;
}
- db.close();
- return status;
- } else {
- Slog.w(TAG, "Failed to persist sound model to database");
- db.close();
- return false;
}
}
- private boolean addOrUpdateKeyphrase(SQLiteDatabase db, UUID modelId, Keyphrase keyphrase) {
+ private boolean addOrUpdateKeyphraseLocked(
+ SQLiteDatabase db, UUID modelId, Keyphrase keyphrase) {
ContentValues values = new ContentValues();
values.put(KeyphraseContract.KEY_ID, keyphrase.id);
values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModes);
@@ -143,62 +146,66 @@
* Deletes the sound model and associated keyphrases.
*/
public boolean deleteKeyphraseSoundModel(UUID uuid) {
- SQLiteDatabase db = getWritableDatabase();
- String modelId = uuid.toString();
- String soundModelClause = SoundModelContract.KEY_ID + "=" + modelId;
- boolean status = true;
- if (db.delete(SoundModelContract.TABLE, soundModelClause, null) == 0) {
- Slog.w(TAG, "No sound models deleted from the database");
- status = false;
+ synchronized(this) {
+ SQLiteDatabase db = getWritableDatabase();
+ String modelId = uuid.toString();
+ String soundModelClause = SoundModelContract.KEY_ID + "=" + modelId;
+ boolean status = true;
+ if (db.delete(SoundModelContract.TABLE, soundModelClause, null) == 0) {
+ Slog.w(TAG, "No sound models deleted from the database");
+ status = false;
+ }
+ String keyphraseClause = KeyphraseContract.KEY_SOUND_MODEL_ID + "=" + modelId;
+ if (db.delete(KeyphraseContract.TABLE, keyphraseClause, null) == 0) {
+ Slog.w(TAG, "No keyphrases deleted from the database");
+ status = false;
+ }
+ db.close();
+ return status;
}
- String keyphraseClause = KeyphraseContract.KEY_SOUND_MODEL_ID + "=" + modelId;
- if (db.delete(KeyphraseContract.TABLE, keyphraseClause, null) == 0) {
- Slog.w(TAG, "No keyphrases deleted from the database");
- status = false;
- }
- db.close();
- return status;
}
/**
* Lists all the keyphrase sound models currently registered with the system.
*/
public List<KeyphraseSoundModel> getKephraseSoundModels() {
- List<KeyphraseSoundModel> models = new ArrayList<>();
- String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE;
- SQLiteDatabase db = getReadableDatabase();
- Cursor c = db.rawQuery(selectQuery, null);
-
- // looping through all rows and adding to list
- if (c.moveToFirst()) {
- do {
- int type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
- if (type != SoundTrigger.SoundModel.TYPE_KEYPHRASE) {
- // Ignore non-keyphrase sound models.
- continue;
- }
- String id = c.getString(c.getColumnIndex(SoundModelContract.KEY_ID));
- byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
- // Get all the keyphrases for this this sound model.
- // Validate the sound model.
- if (id == null) {
- Slog.w(TAG, "Ignoring sound model since it doesn't specify an ID");
- continue;
- }
- KeyphraseSoundModel model = new KeyphraseSoundModel(
- UUID.fromString(id), data, getKeyphrasesForSoundModel(db, id));
- if (DBG) {
- Slog.d(TAG, "Adding model: " + model);
- }
- models.add(model);
- } while (c.moveToNext());
+ synchronized(this) {
+ List<KeyphraseSoundModel> models = new ArrayList<>();
+ String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE;
+ SQLiteDatabase db = getReadableDatabase();
+ Cursor c = db.rawQuery(selectQuery, null);
+
+ // looping through all rows and adding to list
+ if (c.moveToFirst()) {
+ do {
+ int type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
+ if (type != SoundTrigger.SoundModel.TYPE_KEYPHRASE) {
+ // Ignore non-keyphrase sound models.
+ continue;
+ }
+ String id = c.getString(c.getColumnIndex(SoundModelContract.KEY_ID));
+ byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
+ // Get all the keyphrases for this this sound model.
+ // Validate the sound model.
+ if (id == null) {
+ Slog.w(TAG, "Ignoring sound model since it doesn't specify an ID");
+ continue;
+ }
+ KeyphraseSoundModel model = new KeyphraseSoundModel(
+ UUID.fromString(id), data, getKeyphrasesForSoundModelLocked(db, id));
+ if (DBG) {
+ Slog.d(TAG, "Adding model: " + model);
+ }
+ models.add(model);
+ } while (c.moveToNext());
+ }
+ c.close();
+ db.close();
+ return models;
}
- c.close();
- db.close();
- return models;
}
- private Keyphrase[] getKeyphrasesForSoundModel(SQLiteDatabase db, String modelId) {
+ private Keyphrase[] getKeyphrasesForSoundModelLocked(SQLiteDatabase db, String modelId) {
List<Keyphrase> keyphrases = new ArrayList<>();
String selectQuery = "SELECT * FROM " + KeyphraseContract.TABLE
+ " WHERE " + KeyphraseContract.KEY_SOUND_MODEL_ID + " = '" + modelId + "'";
@@ -243,7 +250,7 @@
}
- private String getCommaSeparatedString(int[] users) {
+ private static String getCommaSeparatedString(int[] users) {
if (users == null || users.length == 0) {
return "";
}
@@ -255,7 +262,7 @@
return csv.substring(0, csv.length() - 1);
}
- private int[] getArrayForCommaSeparatedString(String text) {
+ private static int[] getArrayForCommaSeparatedString(String text) {
if (TextUtils.isEmpty(text)) {
return null;
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 2ce4971..5d9e107 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -266,13 +266,13 @@
+ Manifest.permission.MANAGE_VOICE_KEYPHRASES);
}
}
+ }
- final long caller = Binder.clearCallingIdentity();
- try {
- return mDbHelper.getKephraseSoundModels();
- } finally {
- Binder.restoreCallingIdentity(caller);
- }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mDbHelper.getKephraseSoundModels();
+ } finally {
+ Binder.restoreCallingIdentity(caller);
}
}
@@ -287,26 +287,31 @@
if (model == null) {
throw new IllegalArgumentException("Model must not be null");
}
+ }
- final long caller = Binder.clearCallingIdentity();
- try {
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ boolean success = false;
+ if (model.keyphrases == null) {
// If the keyphrases are not present in the model, delete the model.
- if (model.keyphrases == null) {
- if (mDbHelper.deleteKeyphraseSoundModel(model.uuid)) {
- return SoundTriggerHelper.STATUS_OK;
- } else {
- return SoundTriggerHelper.STATUS_ERROR;
- }
- } else {
- if (mDbHelper.addOrUpdateKeyphraseSoundModel(model)) {
- return SoundTriggerHelper.STATUS_OK;
- } else {
- return SoundTriggerHelper.STATUS_ERROR;
+ success = mDbHelper.deleteKeyphraseSoundModel(model.uuid);
+ } else {
+ // Else update the model.
+ success = mDbHelper.addOrUpdateKeyphraseSoundModel(model);
+ }
+ if (success) {
+ synchronized (this) {
+ // Notify the voice interaction service of a change in sound models.
+ if (mImpl != null && mImpl.mService != null) {
+ mImpl.notifySoundModelsChangedLocked();
}
}
- } finally {
- Binder.restoreCallingIdentity(caller);
+ return SoundTriggerHelper.STATUS_OK;
+ } else {
+ return SoundTriggerHelper.STATUS_ERROR;
}
+ } finally {
+ Binder.restoreCallingIdentity(caller);
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 54af5d4..94f227c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -96,6 +96,13 @@
@Override
public void onServiceDisconnected(ComponentName name) {
+ try {
+ if (mService != null) {
+ mService.shutdown();
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in shutdown", e);
+ }
mService = null;
}
};
@@ -308,4 +315,15 @@
mContext.unregisterReceiver(mBroadcastReceiver);
}
}
+
+ void notifySoundModelsChangedLocked() {
+ if (mService == null) {
+ Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
+ }
+ try {
+ mService.soundModelsChanged();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException while calling soundModelsChanged", e);
+ }
+ }
}
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
index f982a44..ad5c614 100644
--- a/telecomm/java/android/telecomm/Call.java
+++ b/telecomm/java/android/telecomm/Call.java
@@ -581,58 +581,59 @@
}
/** {@hide} */
- final void internalUpdate(InCallCall inCallCall) {
+ final void internalUpdate(ParcelableCall parcelableCall) {
// First, we update the internal state as far as possible before firing any updates.
Details details = new Details(
- inCallCall.getHandle(),
- inCallCall.getHandlePresentation(),
- inCallCall.getCallerDisplayName(),
- inCallCall.getCallerDisplayNamePresentation(),
- inCallCall.getAccountHandle(),
- inCallCall.getCapabilities(),
- inCallCall.getDisconnectCauseCode(),
- inCallCall.getDisconnectCauseMsg(),
- inCallCall.getConnectTimeMillis(),
- inCallCall.getGatewayInfo(),
- inCallCall.getVideoState(),
- inCallCall.getStatusHints());
+ parcelableCall.getHandle(),
+ parcelableCall.getHandlePresentation(),
+ parcelableCall.getCallerDisplayName(),
+ parcelableCall.getCallerDisplayNamePresentation(),
+ parcelableCall.getAccountHandle(),
+ parcelableCall.getCapabilities(),
+ parcelableCall.getDisconnectCauseCode(),
+ parcelableCall.getDisconnectCauseMsg(),
+ parcelableCall.getConnectTimeMillis(),
+ parcelableCall.getGatewayInfo(),
+ parcelableCall.getVideoState(),
+ parcelableCall.getStatusHints());
boolean detailsChanged = !Objects.equals(mDetails, details);
if (detailsChanged) {
mDetails = details;
}
boolean cannedTextResponsesChanged = false;
- if (mCannedTextResponses == null && inCallCall.getCannedSmsResponses() != null
- && !inCallCall.getCannedSmsResponses().isEmpty()) {
- mCannedTextResponses = Collections.unmodifiableList(inCallCall.getCannedSmsResponses());
+ if (mCannedTextResponses == null && parcelableCall.getCannedSmsResponses() != null
+ && !parcelableCall.getCannedSmsResponses().isEmpty()) {
+ mCannedTextResponses =
+ Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
}
boolean callVideoProviderChanged = false;
try {
callVideoProviderChanged =
- !Objects.equals(mCallVideoProvider, inCallCall.getCallVideoProvider());
+ !Objects.equals(mCallVideoProvider, parcelableCall.getCallVideoProvider());
if (callVideoProviderChanged) {
- mCallVideoProvider = inCallCall.getCallVideoProvider();
+ mCallVideoProvider = parcelableCall.getCallVideoProvider();
}
} catch (RemoteException e) {
}
- int state = stateFromInCallCallState(inCallCall.getState());
+ int state = stateFromParcelableCallState(parcelableCall.getState());
boolean stateChanged = mState != state;
if (stateChanged) {
mState = state;
}
- if (inCallCall.getParentCallId() != null) {
- mParent = mPhone.internalGetCallByTelecommId(inCallCall.getParentCallId());
+ if (parcelableCall.getParentCallId() != null) {
+ mParent = mPhone.internalGetCallByTelecommId(parcelableCall.getParentCallId());
}
mChildren.clear();
- if (inCallCall.getChildCallIds() != null) {
- for (int i = 0; i < inCallCall.getChildCallIds().size(); i++) {
+ if (parcelableCall.getChildCallIds() != null) {
+ for (int i = 0; i < parcelableCall.getChildCallIds().size(); i++) {
mChildren.add(mPhone.internalGetCallByTelecommId(
- inCallCall.getChildCallIds().get(i)));
+ parcelableCall.getChildCallIds().get(i)));
}
}
@@ -749,8 +750,8 @@
}
}
- private int stateFromInCallCallState(CallState inCallCallState) {
- switch (inCallCallState) {
+ private int stateFromParcelableCallState(CallState parcelableCallState) {
+ switch (parcelableCallState) {
case NEW:
return STATE_NEW;
case PRE_DIAL_WAIT:
@@ -768,7 +769,7 @@
case ABORTED:
return STATE_DISCONNECTED;
default:
- Log.wtf(this, "Unrecognized CallState %s", inCallCallState);
+ Log.wtf(this, "Unrecognized CallState %s", parcelableCallState);
return STATE_NEW;
}
}
diff --git a/telecomm/java/android/telecomm/CallVideoClient.java b/telecomm/java/android/telecomm/CallVideoClient.java
index fb970dc..00473ff 100644
--- a/telecomm/java/android/telecomm/CallVideoClient.java
+++ b/telecomm/java/android/telecomm/CallVideoClient.java
@@ -169,7 +169,7 @@
private final CallVideoClientBinder mBinder;
- protected CallVideoClient() {
+ public CallVideoClient() {
mBinder = new CallVideoClientBinder();
}
diff --git a/telecomm/java/android/telecomm/CallVideoProvider.java b/telecomm/java/android/telecomm/CallVideoProvider.java
index 2a04724..443a5b6 100644
--- a/telecomm/java/android/telecomm/CallVideoProvider.java
+++ b/telecomm/java/android/telecomm/CallVideoProvider.java
@@ -146,7 +146,7 @@
private final CallVideoProviderHandler mMessageHandler = new CallVideoProviderHandler();
private final CallVideoProviderBinder mBinder;
- protected CallVideoProvider() {
+ public CallVideoProvider() {
mBinder = new CallVideoProviderBinder();
}
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index d4af50c..02af68e 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -83,7 +83,7 @@
/**
* Create a new Connection.
*/
- protected Connection() {}
+ public Connection() {}
/**
* @return The handle (e.g., phone number) to which this Connection is currently communicating.
@@ -419,7 +419,7 @@
/**
* TODO(santoscordon): Needs documentation.
*/
- public final void setDestroyed() {
+ public final void destroy() {
// It is possible that onDestroy() will trigger the listener to remove itself which will
// result in a concurrent modification exception. To counteract this we make a copy of the
// listeners and iterate on that.
@@ -484,7 +484,7 @@
*
* @param state The new call audio state.
*/
- protected void onSetAudioState(CallAudioState state) {}
+ public void onSetAudioState(CallAudioState state) {}
/**
* Notifies this Connection of an internal state change. This method is called before the
@@ -492,44 +492,44 @@
*
* @param state The new state, a {@link Connection.State} member.
*/
- protected void onSetState(int state) {}
+ public void onSetState(int state) {}
/**
* Notifies this Connection of a request to play a DTMF tone.
*
* @param c A DTMF character.
*/
- protected void onPlayDtmfTone(char c) {}
+ public void onPlayDtmfTone(char c) {}
/**
* Notifies this Connection of a request to stop any currently playing DTMF tones.
*/
- protected void onStopDtmfTone() {}
+ public void onStopDtmfTone() {}
/**
* Notifies this Connection of a request to disconnect.
*/
- protected void onDisconnect() {}
+ public void onDisconnect() {}
/**
* Notifies this Connection of a request to disconnect.
*/
- protected void onSeparate() {}
+ public void onSeparate() {}
/**
* Notifies this Connection of a request to abort.
*/
- protected void onAbort() {}
+ public void onAbort() {}
/**
* Notifies this Connection of a request to hold.
*/
- protected void onHold() {}
+ public void onHold() {}
/**
* Notifies this Connection of a request to exit a hold state.
*/
- protected void onUnhold() {}
+ public void onUnhold() {}
/**
* Notifies this Connection, which is in {@link State#RINGING}, of
@@ -537,34 +537,34 @@
*
* @param videoState The video state in which to answer the call.
*/
- protected void onAnswer(int videoState) {}
+ public void onAnswer(int videoState) {}
/**
* Notifies this Connection, which is in {@link State#RINGING}, of
* a request to reject.
*/
- protected void onReject() {}
+ public void onReject() {}
/**
* Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
*/
- protected void onPostDialContinue(boolean proceed) {}
+ public void onPostDialContinue(boolean proceed) {}
/**
* Swap this call with a background call. This is used for calls that don't support hold,
* e.g. CDMA.
*/
- protected void onSwapWithBackgroundCall() {}
+ public void onSwapWithBackgroundCall() {}
/**
* TODO(santoscordon): Needs documentation.
*/
- protected void onChildrenChanged(List<Connection> children) {}
+ public void onChildrenChanged(List<Connection> children) {}
/**
* Called when the phone account UI was clicked.
*/
- protected void onPhoneAccountClicked() {}
+ public void onPhoneAccountClicked() {}
private void addChild(Connection connection) {
Log.d(this, "adding child %s", connection);
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index c8a548d..d5b39cf 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -16,10 +16,12 @@
package android.telecomm;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.app.PendingIntent;
import android.app.Service;
-import android.content.Intent;
import android.content.ComponentName;
+import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -42,6 +44,12 @@
* Android device.
*/
public abstract class ConnectionService extends Service {
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE = "android.telecomm.ConnectionService";
+
// Flag controlling whether PII is emitted into the logs
private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
private static final Connection NULL_CONNECTION = new Connection() {};
@@ -424,14 +432,20 @@
public void onSuccess(ConnectionRequest request, Connection connection) {
Log.d(this, "adapter handleCreateConnectionSuccessful %s",
request.getCallId());
- mAdapter.handleCreateConnectionSuccessful(request);
addConnection(request.getCallId(), connection);
-
- // TODO: onSuccess should pass through the entire state of the connection instead of
- // having to set it like this afterwards. Also, it would eliminate the hack of
- // having to change the request object that we pass back.
- mConnectionListener.onCallCapabilitiesChanged(
- connection, connection.getCallCapabilities());
+ mAdapter.handleCreateConnectionSuccessful(
+ request,
+ new ParcelableConnection(
+ request.getAccountHandle(),
+ connection.getState(),
+ connection.getCallCapabilities(),
+ connection.getHandle(),
+ connection.getHandlePresentation(),
+ connection.getCallerDisplayName(),
+ connection.getCallerDisplayNamePresentation(),
+ connection.getCallVideoProvider() == null ?
+ null : connection.getCallVideoProvider().getInterface(),
+ connection.getVideoState()));
}
@Override
@@ -639,7 +653,7 @@
* @param request Details about the incoming call.
* @param callback A callback for providing the result.
*/
- protected void onCreateIncomingConnection(
+ public void onCreateIncomingConnection(
ConnectionRequest request,
CreateConnectionResponse<Connection> callback) {}
@@ -649,7 +663,7 @@
* @param request Details about the outgoing call.
* @param callback A callback for providing the result.
*/
- protected void onCreateOutgoingConnection(
+ public void onCreateOutgoingConnection(
ConnectionRequest request,
CreateConnectionResponse<Connection> callback) {}
@@ -663,7 +677,7 @@
* @param token The token to be passed into the response callback.
* @param callback The callback for providing the potentially-new conference connection.
*/
- protected void onCreateConferenceConnection(
+ public void onCreateConferenceConnection(
String token,
Connection connection,
Response<String, Connection> callback) {}
@@ -673,14 +687,14 @@
*
* @param connection The connection which was added.
*/
- protected void onConnectionAdded(Connection connection) {}
+ public void onConnectionAdded(Connection connection) {}
/**
* Notified that a connection has been removed from this connection service.
*
* @param connection The connection which was removed.
*/
- protected void onConnectionRemoved(Connection connection) {}
+ public void onConnectionRemoved(Connection connection) {}
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
@@ -712,12 +726,6 @@
mIdByConnection.put(connection, callId);
connection.addConnectionListener(mConnectionListener);
onConnectionAdded(connection);
-
- // Trigger listeners for properties set before connection listener was added.
- CallVideoProvider callVideoProvider = connection.getCallVideoProvider();
- if (callVideoProvider != null) {
- connection.setCallVideoProvider(callVideoProvider);
- }
}
private void removeConnection(Connection connection) {
diff --git a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
index 0b8d93d..66e9925 100644
--- a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
@@ -74,10 +74,11 @@
}
}
- void handleCreateConnectionSuccessful(ConnectionRequest request) {
+ void handleCreateConnectionSuccessful(
+ ConnectionRequest request, ParcelableConnection connection) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.handleCreateConnectionSuccessful(request);
+ adapter.handleCreateConnectionSuccessful(request, connection);
} catch (RemoteException e) {
}
}
@@ -277,7 +278,9 @@
void setCallVideoProvider(String callId, CallVideoProvider callVideoProvider) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setCallVideoProvider(callId, callVideoProvider.getInterface());
+ adapter.setCallVideoProvider(
+ callId,
+ callVideoProvider == null ? null : callVideoProvider.getInterface());
} catch (RemoteException e) {
}
}
diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java
index 07f7cfd..9699e2a 100644
--- a/telecomm/java/android/telecomm/InCallService.java
+++ b/telecomm/java/android/telecomm/InCallService.java
@@ -54,10 +54,10 @@
onPhoneCreated(mPhone);
break;
case MSG_ADD_CALL:
- mPhone.internalAddCall((InCallCall) msg.obj);
+ mPhone.internalAddCall((ParcelableCall) msg.obj);
break;
case MSG_UPDATE_CALL:
- mPhone.internalUpdateCall((InCallCall) msg.obj);
+ mPhone.internalUpdateCall((ParcelableCall) msg.obj);
break;
case MSG_SET_POST_DIAL: {
SomeArgs args = (SomeArgs) msg.obj;
@@ -110,12 +110,12 @@
}
@Override
- public void addCall(InCallCall call) {
+ public void addCall(ParcelableCall call) {
mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();
}
@Override
- public void updateCall(InCallCall call) {
+ public void updateCall(ParcelableCall call) {
mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget();
}
diff --git a/telecomm/java/android/telecomm/InCallCall.aidl b/telecomm/java/android/telecomm/ParcelableCall.aidl
similarity index 95%
rename from telecomm/java/android/telecomm/InCallCall.aidl
rename to telecomm/java/android/telecomm/ParcelableCall.aidl
index be2cdf8..8c96638 100644
--- a/telecomm/java/android/telecomm/InCallCall.aidl
+++ b/telecomm/java/android/telecomm/ParcelableCall.aidl
@@ -16,4 +16,4 @@
package android.telecomm;
-parcelable InCallCall;
+parcelable ParcelableCall;
diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/ParcelableCall.java
similarity index 93%
rename from telecomm/java/android/telecomm/InCallCall.java
rename to telecomm/java/android/telecomm/ParcelableCall.java
index 34d09f3..27a5c1d 100644
--- a/telecomm/java/android/telecomm/InCallCall.java
+++ b/telecomm/java/android/telecomm/ParcelableCall.java
@@ -29,8 +29,9 @@
/**
* Information about a call that is used between InCallService and Telecomm.
+ * @hide
*/
-public final class InCallCall implements Parcelable {
+public final class ParcelableCall implements Parcelable {
private final String mId;
private final CallState mState;
private final int mDisconnectCauseCode;
@@ -52,7 +53,7 @@
private final int mVideoState;
/** @hide */
- public InCallCall(
+ public ParcelableCall(
String id,
CallState state,
int disconnectCauseCode,
@@ -214,12 +215,12 @@
return mVideoState;
}
- /** Responsible for creating InCallCall objects for deserialized Parcels. */
- public static final Parcelable.Creator<InCallCall> CREATOR =
- new Parcelable.Creator<InCallCall> () {
+ /** Responsible for creating ParcelableCall objects for deserialized Parcels. */
+ public static final Parcelable.Creator<ParcelableCall> CREATOR =
+ new Parcelable.Creator<ParcelableCall> () {
@Override
- public InCallCall createFromParcel(Parcel source) {
- ClassLoader classLoader = InCallCall.class.getClassLoader();
+ public ParcelableCall createFromParcel(Parcel source) {
+ ClassLoader classLoader = ParcelableCall.class.getClassLoader();
String id = source.readString();
CallState state = CallState.valueOf(source.readString());
int disconnectCauseCode = source.readInt();
@@ -241,7 +242,7 @@
source.readList(childCallIds, classLoader);
StatusHints statusHints = source.readParcelable(classLoader);
int videoState = source.readInt();
- return new InCallCall(id, state, disconnectCauseCode, disconnectCauseMsg,
+ return new ParcelableCall(id, state, disconnectCauseCode, disconnectCauseMsg,
cannedSmsResponses, capabilities, connectTimeMillis, handle, handlePresentation,
callerDisplayName, callerDisplayNamePresentation, gatewayInfo,
accountHandle, callVideoProvider, parentCallId, childCallIds, statusHints,
@@ -249,8 +250,8 @@
}
@Override
- public InCallCall[] newArray(int size) {
- return new InCallCall[size];
+ public ParcelableCall[] newArray(int size) {
+ return new ParcelableCall[size];
}
};
@@ -260,7 +261,7 @@
return 0;
}
- /** Writes InCallCall object into a Parcel. */
+ /** Writes ParcelableCall object into a Parcel. */
@Override
public void writeToParcel(Parcel destination, int flags) {
destination.writeString(mId);
diff --git a/telecomm/java/android/telecomm/InCallCall.aidl b/telecomm/java/android/telecomm/ParcelableConnection.aidl
similarity index 94%
copy from telecomm/java/android/telecomm/InCallCall.aidl
copy to telecomm/java/android/telecomm/ParcelableConnection.aidl
index be2cdf8..e3c3bd2 100644
--- a/telecomm/java/android/telecomm/InCallCall.aidl
+++ b/telecomm/java/android/telecomm/ParcelableConnection.aidl
@@ -16,4 +16,4 @@
package android.telecomm;
-parcelable InCallCall;
+parcelable ParcelableConnection;
diff --git a/telecomm/java/android/telecomm/ParcelableConnection.java b/telecomm/java/android/telecomm/ParcelableConnection.java
new file mode 100644
index 0000000..f730fef
--- /dev/null
+++ b/telecomm/java/android/telecomm/ParcelableConnection.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecomm;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telecomm.ICallVideoProvider;
+
+/**
+ * Information about a connection that is used between Telecomm and the ConnectionService.
+ * This is used to send initial Connection information to Telecomm when the connection is
+ * first created.
+ * @hide
+ */
+public final class ParcelableConnection implements Parcelable {
+ private PhoneAccountHandle mPhoneAccount;
+ private int mState;
+ private int mCapabilities;
+ private Uri mHandle;
+ private int mHandlePresentation;
+ private String mCallerDisplayName;
+ private int mCallerDisplayNamePresentation;
+ private ICallVideoProvider mCallVideoProvider;
+ private int mVideoState;
+
+ /** @hide */
+ public ParcelableConnection(
+ PhoneAccountHandle phoneAccount,
+ int state,
+ int capabilities,
+ Uri handle,
+ int handlePresentation,
+ String callerDisplayName,
+ int callerDisplayNamePresentation,
+ ICallVideoProvider callVideoProvider,
+ int videoState) {
+ mPhoneAccount = phoneAccount;
+ mState = state;
+ mCapabilities = capabilities;
+ mHandle = handle;
+ mHandlePresentation = handlePresentation;
+ mCallerDisplayName = callerDisplayName;
+ mCallerDisplayNamePresentation = callerDisplayNamePresentation;
+ mCallVideoProvider = callVideoProvider;
+ mVideoState = videoState;
+ }
+
+ public PhoneAccountHandle getPhoneAccount() {
+ return mPhoneAccount;
+ }
+
+ public int getState() {
+ return mState;
+ }
+
+ // Bit mask of actions a call supports, values are defined in {@link CallCapabilities}.
+ public int getCapabilities() {
+ return mCapabilities;
+ }
+
+ public Uri getHandle() {
+ return mHandle;
+ }
+
+ public int getHandlePresentation() {
+ return mHandlePresentation;
+ }
+
+ public String getCallerDisplayName() {
+ return mCallerDisplayName;
+ }
+
+ public int getCallerDisplayNamePresentation() {
+ return mCallerDisplayNamePresentation;
+ }
+
+ public ICallVideoProvider getCallVideoProvider() {
+ return mCallVideoProvider;
+ }
+
+ public int getVideoState() {
+ return mVideoState;
+ }
+
+ public static final Parcelable.Creator<ParcelableConnection> CREATOR =
+ new Parcelable.Creator<ParcelableConnection> () {
+ @Override
+ public ParcelableConnection createFromParcel(Parcel source) {
+ ClassLoader classLoader = ParcelableConnection.class.getClassLoader();
+
+ PhoneAccountHandle phoneAccount = source.readParcelable(classLoader);
+ int state = source.readInt();
+ int capabilities = source.readInt();
+ Uri handle = source.readParcelable(classLoader);
+ int handlePresentation = source.readInt();
+ String callerDisplayName = source.readString();
+ int callerDisplayNamePresentation = source.readInt();
+ ICallVideoProvider callVideoProvider =
+ ICallVideoProvider.Stub.asInterface(source.readStrongBinder());
+ int videoState = source.readInt();
+
+ return new ParcelableConnection(
+ phoneAccount,
+ state,
+ capabilities,
+ handle,
+ handlePresentation,
+ callerDisplayName,
+ callerDisplayNamePresentation,
+ callVideoProvider,
+ videoState);
+ }
+
+ @Override
+ public ParcelableConnection[] newArray(int size) {
+ return new ParcelableConnection[size];
+ }
+ };
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Writes ParcelableConnection object into a Parcel. */
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeParcelable(mPhoneAccount, 0);
+ destination.writeInt(mState);
+ destination.writeInt(mCapabilities);
+ destination.writeParcelable(mHandle, 0);
+ destination.writeInt(mHandlePresentation);
+ destination.writeString(mCallerDisplayName);
+ destination.writeInt(mCallerDisplayNamePresentation);
+ destination.writeStrongBinder(
+ mCallVideoProvider != null ? mCallVideoProvider.asBinder() : null);
+ destination.writeInt(mVideoState);
+ }
+}
diff --git a/telecomm/java/android/telecomm/Phone.java b/telecomm/java/android/telecomm/Phone.java
index 0f271c3..ce95acf 100644
--- a/telecomm/java/android/telecomm/Phone.java
+++ b/telecomm/java/android/telecomm/Phone.java
@@ -95,12 +95,12 @@
}
/** {@hide} */
- final void internalAddCall(InCallCall inCallCall) {
- Call call = new Call(this, inCallCall.getId(), mInCallAdapter);
- mCallByTelecommCallId.put(inCallCall.getId(), call);
+ final void internalAddCall(ParcelableCall parcelableCall) {
+ Call call = new Call(this, parcelableCall.getId(), mInCallAdapter);
+ mCallByTelecommCallId.put(parcelableCall.getId(), call);
mCalls.add(call);
- checkCallTree(inCallCall);
- call.internalUpdate(inCallCall);
+ checkCallTree(parcelableCall);
+ call.internalUpdate(parcelableCall);
fireCallAdded(call);
}
@@ -112,11 +112,11 @@
}
/** {@hide} */
- final void internalUpdateCall(InCallCall inCallCall) {
- Call call = mCallByTelecommCallId.get(inCallCall.getId());
+ final void internalUpdateCall(ParcelableCall parcelableCall) {
+ Call call = mCallByTelecommCallId.get(parcelableCall.getId());
if (call != null) {
- checkCallTree(inCallCall);
- call.internalUpdate(inCallCall);
+ checkCallTree(parcelableCall);
+ call.internalUpdate(parcelableCall);
}
}
@@ -246,17 +246,17 @@
}
}
- private void checkCallTree(InCallCall inCallCall) {
- if (inCallCall.getParentCallId() != null &&
- !mCallByTelecommCallId.containsKey(inCallCall.getParentCallId())) {
- Log.wtf(this, "InCallCall %s has nonexistent parent %s",
- inCallCall.getId(), inCallCall.getParentCallId());
+ private void checkCallTree(ParcelableCall parcelableCall) {
+ if (parcelableCall.getParentCallId() != null &&
+ !mCallByTelecommCallId.containsKey(parcelableCall.getParentCallId())) {
+ Log.wtf(this, "ParcelableCall %s has nonexistent parent %s",
+ parcelableCall.getId(), parcelableCall.getParentCallId());
}
- if (inCallCall.getChildCallIds() != null) {
- for (int i = 0; i < inCallCall.getChildCallIds().size(); i++) {
- if (!mCallByTelecommCallId.containsKey(inCallCall.getChildCallIds().get(i))) {
- Log.wtf(this, "InCallCall %s has nonexistent child %s",
- inCallCall.getId(), inCallCall.getChildCallIds().get(i));
+ if (parcelableCall.getChildCallIds() != null) {
+ for (int i = 0; i < parcelableCall.getChildCallIds().size(); i++) {
+ if (!mCallByTelecommCallId.containsKey(parcelableCall.getChildCallIds().get(i))) {
+ Log.wtf(this, "ParcelableCall %s has nonexistent child %s",
+ parcelableCall.getId(), parcelableCall.getChildCallIds().get(i));
}
}
}
diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java
index 3f4dbfe..1f9071e 100644
--- a/telecomm/java/android/telecomm/PhoneAccount.java
+++ b/telecomm/java/android/telecomm/PhoneAccount.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.Resources.NotFoundException;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Parcel;
@@ -52,6 +53,8 @@
* calls from or use the built-in telephony stack to place its calls.
* <p>
* See {@link #getCapabilities}
+ *
+ * {@hide}
*/
public static final int CAPABILITY_CALL_PROVIDER = 0x2;
@@ -68,8 +71,8 @@
private final String mSubscriptionNumber;
private final int mCapabilities;
private final int mIconResId;
- private final String mLabel;
- private final String mShortDescription;
+ private final CharSequence mLabel;
+ private final CharSequence mShortDescription;
private boolean mVideoCallingSupported;
public PhoneAccount(
@@ -78,8 +81,8 @@
String subscriptionNumber,
int capabilities,
int iconResId,
- String label,
- String shortDescription,
+ CharSequence label,
+ CharSequence shortDescription,
boolean supportsVideoCalling) {
mAccountHandle = account;
mHandle = handle;
@@ -133,11 +136,11 @@
}
/**
- * A short string label describing a {@code PhoneAccount}.
+ * A short label describing a {@code PhoneAccount}.
*
* @return A label for this {@code PhoneAccount}.
*/
- public String getLabel() {
+ public CharSequence getLabel() {
return mLabel;
}
@@ -146,7 +149,7 @@
*
* @return A description for this {@code PhoneAccount}.
*/
- public String getShortDescription() {
+ public CharSequence getShortDescription() {
return mShortDescription;
}
@@ -179,7 +182,7 @@
}
try {
return packageContext.getResources().getDrawable(resId);
- } catch (MissingResourceException e) {
+ } catch (NotFoundException|MissingResourceException e) {
Log.e(this, e, "Cannot find icon %d in package %s",
resId, mAccountHandle.getComponentName().getPackageName());
return null;
@@ -211,8 +214,8 @@
out.writeString(mSubscriptionNumber);
out.writeInt(mCapabilities);
out.writeInt(mIconResId);
- out.writeString(mLabel);
- out.writeString(mShortDescription);
+ out.writeCharSequence(mLabel);
+ out.writeCharSequence(mShortDescription);
out.writeInt(mVideoCallingSupported ? 1 : 0);
}
@@ -235,8 +238,8 @@
mSubscriptionNumber = in.readString();
mCapabilities = in.readInt();
mIconResId = in.readInt();
- mLabel = in.readString();
- mShortDescription = in.readString();
+ mLabel = in.readCharSequence();
+ mShortDescription = in.readCharSequence();
mVideoCallingSupported = in.readInt() == 1;
}
}
diff --git a/telecomm/java/android/telecomm/RemoteCallVideoClient.java b/telecomm/java/android/telecomm/RemoteCallVideoClient.java
index 8024831..08d1391 100644
--- a/telecomm/java/android/telecomm/RemoteCallVideoClient.java
+++ b/telecomm/java/android/telecomm/RemoteCallVideoClient.java
@@ -26,17 +26,20 @@
/**
* Remote class to invoke callbacks in InCallUI related to supporting video in calls.
*/
-public class RemoteCallVideoClient implements IBinder.DeathRecipient {
+public class RemoteCallVideoClient {
private final ICallVideoClient mCallVideoClient;
+ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ mCallVideoClient.asBinder().unlinkToDeath(this, 0);
+ }
+ };
+
+ /** {@hide} */
RemoteCallVideoClient(ICallVideoClient callVideoProvider) throws RemoteException {
mCallVideoClient = callVideoProvider;
- mCallVideoClient.asBinder().linkToDeath(this, 0);
- }
-
- @Override
- public void binderDied() {
- mCallVideoClient.asBinder().unlinkToDeath(this, 0);
+ mCallVideoClient.asBinder().linkToDeath(mDeathRecipient, 0);
}
/**
@@ -49,9 +52,11 @@
*
* @param videoCallProfile The requested video call profile.
*/
- public void receiveSessionModifyRequest(VideoCallProfile videoCallProfile)
- throws RemoteException {
- mCallVideoClient.receiveSessionModifyRequest(videoCallProfile);
+ public void receiveSessionModifyRequest(VideoCallProfile videoCallProfile) {
+ try {
+ mCallVideoClient.receiveSessionModifyRequest(videoCallProfile);
+ } catch (RemoteException e) {
+ }
}
/**
@@ -66,9 +71,13 @@
* @param requestedProfile The original request which was sent to the remote device.
* @param responseProfile The actual profile changes made by the remote device.
*/
- public void receiveSessionModifyResponse(int status, VideoCallProfile requestedProfile,
- VideoCallProfile responseProfile) throws RemoteException {
- mCallVideoClient.receiveSessionModifyResponse(status, requestedProfile, responseProfile);
+ public void receiveSessionModifyResponse(
+ int status, VideoCallProfile requestedProfile, VideoCallProfile responseProfile) {
+ try {
+ mCallVideoClient.receiveSessionModifyResponse(
+ status, requestedProfile, responseProfile);
+ } catch (RemoteException e) {
+ }
}
/**
@@ -81,8 +90,11 @@
*
* @param event The event.
*/
- public void handleCallSessionEvent(int event) throws RemoteException {
- mCallVideoClient.handleCallSessionEvent(event);
+ public void handleCallSessionEvent(int event) {
+ try {
+ mCallVideoClient.handleCallSessionEvent(event);
+ } catch (RemoteException e) {
+ }
}
/**
@@ -92,8 +104,11 @@
* @param width The updated peer video width.
* @param height The updated peer video height.
*/
- public void updatePeerDimensions(int width, int height) throws RemoteException {
- mCallVideoClient.updatePeerDimensions(width, height);
+ public void updatePeerDimensions(int width, int height) {
+ try {
+ mCallVideoClient.updatePeerDimensions(width, height);
+ } catch (RemoteException e) {
+ }
}
/**
@@ -101,8 +116,11 @@
*
* @param dataUsage The updated data usage.
*/
- public void updateCallDataUsage(int dataUsage) throws RemoteException {
- mCallVideoClient.updateCallDataUsage(dataUsage);
+ public void updateCallDataUsage(int dataUsage) {
+ try {
+ mCallVideoClient.updateCallDataUsage(dataUsage);
+ } catch (RemoteException e) {
+ }
}
/**
@@ -110,8 +128,10 @@
*
* @param callCameraCapabilities The changed camera capabilities.
*/
- public void handleCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities)
- throws RemoteException {
- mCallVideoClient.handleCameraCapabilitiesChange(callCameraCapabilities);
+ public void handleCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities) {
+ try {
+ mCallVideoClient.handleCameraCapabilitiesChange(callCameraCapabilities);
+ } catch (RemoteException e) {
+ }
}
-}
+}
\ No newline at end of file
diff --git a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
index 170cf04..b8b8b9d 100644
--- a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
+++ b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
@@ -59,8 +59,11 @@
*
* @param cameraId The id of the camera.
*/
- public void setCamera(String cameraId) throws RemoteException {
- mCallVideoProvider.setCamera(cameraId);
+ public void setCamera(String cameraId) {
+ try {
+ mCallVideoProvider.setCamera(cameraId);
+ } catch (RemoteException e) {
+ }
}
/**
@@ -107,8 +110,11 @@
*
* @param value The camera zoom ratio.
*/
- public void setZoom(float value) throws RemoteException {
- mCallVideoProvider.setZoom(value);
+ public void setZoom(float value) {
+ try {
+ mCallVideoProvider.setZoom(value);
+ } catch (RemoteException e) {
+ }
}
/**
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index 29461af..10569ab 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -19,18 +19,17 @@
import android.app.PendingIntent;
import android.content.ComponentName;
import android.net.Uri;
-import android.os.IBinder.DeathRecipient;
import android.os.Handler;
+import android.os.IBinder.DeathRecipient;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.DisconnectCause;
-
import android.text.TextUtils;
import com.android.internal.os.SomeArgs;
+import com.android.internal.telecomm.ICallVideoProvider;
import com.android.internal.telecomm.IConnectionService;
import com.android.internal.telecomm.IConnectionServiceAdapter;
-import com.android.internal.telecomm.ICallVideoProvider;
import com.android.internal.telecomm.RemoteServiceCallback;
import java.util.LinkedList;
@@ -80,11 +79,27 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL: {
- ConnectionRequest request = (ConnectionRequest) msg.obj;
- if (isPendingConnection(request.getCallId())) {
- mConnection = new RemoteConnection(mConnectionService, request.getCallId());
- mPendingResponse.onSuccess(request, mConnection);
- clearPendingInformation();
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ ConnectionRequest request = (ConnectionRequest) args.arg1;
+ if (isPendingConnection(request.getCallId())) {
+ ParcelableConnection parcel = (ParcelableConnection) args.arg2;
+ mConnection = new RemoteConnection(
+ mConnectionService, request.getCallId());
+ mConnection.setState(parcel.getState());
+ mConnection.setCallCapabilities(parcel.getCapabilities());
+ mConnection.setHandle(
+ parcel.getHandle(), parcel.getHandlePresentation());
+ mConnection.setCallerDisplayName(
+ parcel.getCallerDisplayName(),
+ parcel.getCallerDisplayNamePresentation());
+ // TODO: Do we need to support video providers for remote connections?
+
+ mPendingResponse.onSuccess(request, mConnection);
+ clearPendingInformation();
+ }
+ } finally {
+ args.recycle();
}
break;
}
@@ -242,8 +257,12 @@
private final IConnectionServiceAdapter mAdapter = new IConnectionServiceAdapter.Stub() {
@Override
- public void handleCreateConnectionSuccessful(ConnectionRequest request) {
- mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL, request).sendToTarget();
+ public void handleCreateConnectionSuccessful(
+ ConnectionRequest request, ParcelableConnection connection) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = request;
+ args.arg2 = connection;
+ mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL, args).sendToTarget();
}
@Override
diff --git a/telecomm/java/android/telecomm/StatusHints.java b/telecomm/java/android/telecomm/StatusHints.java
index 5e64bff..50f525a 100644
--- a/telecomm/java/android/telecomm/StatusHints.java
+++ b/telecomm/java/android/telecomm/StatusHints.java
@@ -34,11 +34,11 @@
public final class StatusHints implements Parcelable {
private final ComponentName mComponentName;
- private final String mLabel;
+ private final CharSequence mLabel;
private final int mIconId;
private final Bundle mExtras;
- public StatusHints(ComponentName componentName, String label, int iconId, Bundle extras) {
+ public StatusHints(ComponentName componentName, CharSequence label, int iconId, Bundle extras) {
mComponentName = componentName;
mLabel = label;
mIconId = iconId;
@@ -55,7 +55,7 @@
/**
* @return The label displayed in the in-call UI.
*/
- public String getLabel() {
+ public CharSequence getLabel() {
return mLabel;
}
@@ -88,7 +88,7 @@
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeParcelable(mComponentName, flags);
- out.writeString(mLabel);
+ out.writeCharSequence(mLabel);
out.writeInt(mIconId);
out.writeParcelable(mExtras, 0);
}
@@ -106,7 +106,7 @@
private StatusHints(Parcel in) {
mComponentName = in.readParcelable(getClass().getClassLoader());
- mLabel = in.readString();
+ mLabel = in.readCharSequence();
mIconId = in.readInt();
mExtras = (Bundle) in.readParcelable(getClass().getClassLoader());
}
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index 4060742..49f95a6 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -17,6 +17,8 @@
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
+
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
@@ -27,33 +29,26 @@
/**
* Provides access to Telecomm-related functionality.
- * TODO(santoscordon): Move this all into PhoneManager.
+ * TODO: Move this all into PhoneManager.
*/
public class TelecommManager {
/**
- * <p>Activity action: Starts the UI for handing an incoming call. This intent starts the
- * in-call UI by notifying the Telecomm system that an incoming call exists for a specific call
- * service (see {@link ConnectionService}). Telecomm reads the Intent extras
- * to find and bind to the appropriate {@link ConnectionService} which
- * Telecomm will ultimately use to control and get information about the call.</p>
- *
- * <p>Input: get*Extra field {@link #EXTRA_PHONE_ACCOUNT_HANDLE} contains the component name of the
- * {@link ConnectionService} that Telecomm should bind to. Telecomm will then
+ * Activity action: Starts the UI for handing an incoming call. This intent starts the in-call
+ * UI by notifying the Telecomm system that an incoming call exists for a specific call service
+ * (see {@link android.telecomm.ConnectionService}). Telecomm reads the Intent extras to find
+ * and bind to the appropriate {@link android.telecomm.ConnectionService} which Telecomm will
+ * ultimately use to control and get information about the call.
+ * <p>
+ * Input: get*Extra field {@link #EXTRA_PHONE_ACCOUNT_HANDLE} contains the component name of the
+ * {@link android.telecomm.ConnectionService} that Telecomm should bind to. Telecomm will then
* ask the connection service for more information about the call prior to showing any UI.
*
- * TODO(santoscordon): Needs permissions.
- * TODO(santoscordon): Consider moving this into a simple method call on a system service.
+ * @hide
*/
public static final String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
/**
- * The service action used to bind to {@link android.telecomm.ConnectionService}
- * implementations.
- */
- public static final String ACTION_CONNECTION_SERVICE = ConnectionService.class.getName();
-
- /**
* The {@link android.content.Intent} action used to configure a
* {@link android.telecomm.ConnectionService}.
*/
@@ -61,6 +56,12 @@
"android.intent.action.CONNECTION_SERVICE_CONFIGURE";
/**
+ * The {@link android.content.Intent} action used to show the call settings page.
+ */
+ public static final String ACTION_SHOW_CALL_SETTINGS =
+ "android.telecomm.intent.action.SHOW_CALL_SETTINGS";
+
+ /**
* Optional extra for {@link android.content.Intent#ACTION_CALL} containing a boolean that
* determines whether the speakerphone should be automatically turned on for an outgoing call.
*/
@@ -70,7 +71,8 @@
/**
* Optional extra for {@link android.content.Intent#ACTION_CALL} containing an integer that
* determines the desired video state for an outgoing call.
- * Valid options: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
+ * Valid options:
+ * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
* {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
* {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED},
* {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED}.
@@ -79,21 +81,21 @@
"android.intent.extra.START_CALL_WITH_VIDEO_STATE";
/**
- * The extra used with an {@link android.content.Intent#ACTION_CALL},
- * {@link #ACTION_INCOMING_CALL}, {@link android.content.Intent#ACTION_DIAL} {@code Intent} to
- * specify a {@link PhoneAccountHandle} to use when making the call.
- *
+ * The extra used with an {@link android.content.Intent#ACTION_CALL} and
+ * {@link android.content.Intent#ACTION_DIAL} {@code Intent} to specify a
+ * {@link PhoneAccountHandle} to use when making the call.
* <p class="note">
- * Retrieve with
- * {@link android.content.Intent#getParcelableExtra(String)}.
+ * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
*/
public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
"android.intent.extra.PHONE_ACCOUNT_HANDLE";
/**
- * Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link android.os.Bundle}
- * which contains metadata about the call. This {@link android.os.Bundle} will be returned to
- * the {@link android.telecomm.ConnectionService}.
+ * Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link Bundle} which contains
+ * metadata about the call. This {@link Bundle} will be returned to the
+ * {@link ConnectionService}.
+ *
+ * @hide
*/
public static final String EXTRA_INCOMING_CALL_EXTRAS =
"android.intent.extra.INCOMING_CALL_EXTRAS";
@@ -123,10 +125,9 @@
* The number which the party on the other side of the line will see (and use to return the
* call).
* <p>
- * {@link ConnectionService}s which interact with
- * {@link RemoteConnection}s should only populate this if the
- * {@link android.telephony.TelephonyManager#getLine1Number()} value, as that is the user's
- * expected caller ID.
+ * {@link ConnectionService}s which interact with {@link RemoteConnection}s should only populate
+ * this if the {@link android.telephony.TelephonyManager#getLine1Number()} value, as that is the
+ * user's expected caller ID.
*/
public static final String EXTRA_CALL_BACK_NUMBER = "android.telecomm.extra.CALL_BACK_NUMBER";
@@ -178,8 +179,8 @@
/**
* Broadcast intent action indicating that the current TTY mode has changed. An intent extra
* provides this state as an int.
- * @see #EXTRA_CURRENT_TTY_MODE
*
+ * @see #EXTRA_CURRENT_TTY_MODE
* @hide
*/
public static final String ACTION_CURRENT_TTY_MODE_CHANGED =
@@ -199,22 +200,19 @@
"android.telecomm.intent.extra.CURRENT_TTY_MODE";
/**
- * Broadcast intent action indicating that the TTY preferred operating mode
- * has changed. An intent extra provides the new mode as an int.
- * @see #EXTRA_TTY_PREFERRED_MODE
+ * Broadcast intent action indicating that the TTY preferred operating mode has changed. An
+ * intent extra provides the new mode as an int.
*
+ * @see #EXTRA_TTY_PREFERRED_MODE
* @hide
*/
public static final String ACTION_TTY_PREFERRED_MODE_CHANGED =
"android.telecomm.intent.action.TTY_PREFERRED_MODE_CHANGED";
/**
- * The lookup key for an int that indicates preferred TTY mode.
- * Valid modes are:
- * - {@link #TTY_MODE_OFF}
- * - {@link #TTY_MODE_FULL}
- * - {@link #TTY_MODE_HCO}
- * - {@link #TTY_MODE_VCO}
+ * The lookup key for an int that indicates preferred TTY mode. Valid modes are: -
+ * {@link #TTY_MODE_OFF} - {@link #TTY_MODE_FULL} - {@link #TTY_MODE_HCO} -
+ * {@link #TTY_MODE_VCO}
*
* @hide
*/
@@ -222,7 +220,7 @@
"android.telecomm.intent.extra.TTY_PREFERRED";
private static final String TAG = "TelecommManager";
-
+
private static final String TELECOMM_SERVICE_NAME = "telecomm";
private final Context mContext;
@@ -247,16 +245,16 @@
}
/**
- * Return the {@link PhoneAccount} which is the user-chosen default for making outgoing
- * phone calls. This {@code PhoneAccount} will always be a member of the list which is
- * returned from calling {@link #getEnabledPhoneAccounts()}.
+ * Return the {@link PhoneAccount} which is the user-chosen default for making outgoing phone
+ * calls. This {@code PhoneAccount} will always be a member of the list which is returned from
+ * calling {@link #getEnabledPhoneAccounts()}.
* <p>
- * Apps must be prepared for this method to return {@code null}, indicating that there
- * currently exists no user-chosen default {@code PhoneAccount}. In this case, apps
- * wishing to initiate a phone call must either create their {@link android.content
- * .Intent#ACTION_CALL} or {@link android.content.Intent#ACTION_DIAL} {@code Intent} with no
- * {@link TelecommManager#EXTRA_PHONE_ACCOUNT_HANDLE}, or present the user with an affordance
- * to select one of the elements of {@link #getEnabledPhoneAccounts()}.
+ * Apps must be prepared for this method to return {@code null}, indicating that there currently
+ * exists no user-chosen default {@code PhoneAccount}. In this case, apps wishing to initiate a
+ * phone call must either create their {@link android.content .Intent#ACTION_CALL} or
+ * {@link android.content.Intent#ACTION_DIAL} {@code Intent} with no
+ * {@link TelecommManager#EXTRA_PHONE_ACCOUNT_HANDLE}, or present the user with an affordance to
+ * select one of the elements of {@link #getEnabledPhoneAccounts()}.
* <p>
* An {@link android.content.Intent#ACTION_CALL} or {@link android.content.Intent#ACTION_DIAL}
* {@code Intent} with no {@link TelecommManager#EXTRA_PHONE_ACCOUNT_HANDLE} is valid, and
@@ -293,11 +291,20 @@
}
/**
+ * Determine whether the device has more than one account registered and enabled.
+ *
+ * @return {@code true} if the device has more than one account registered and enabled and
+ * {@code false} otherwise.
+ */
+ public boolean hasMultipleEnabledAccounts() {
+ return getEnabledPhoneAccounts().size() > 1;
+ }
+
+ /**
* Return the {@link PhoneAccount} for a specified {@link PhoneAccountHandle}. Object includes
* resources which can be used in a user interface.
*
* @param account The {@link PhoneAccountHandle}.
- *
* @return The {@link PhoneAccount} object.
*/
public PhoneAccount getPhoneAccount(PhoneAccountHandle account) {
@@ -408,9 +415,9 @@
}
/**
- * Ends an ongoing call. TODO(santoscordon): L-release - need to convert all invocations of
- * ITelecommService#endCall to use this method (clockwork & gearhead).
- *
+ * Ends an ongoing call.
+ * TODO: L-release - need to convert all invocations of ITelecommService#endCall to use this
+ * method (clockwork & gearhead).
* @hide
*/
@SystemApi
@@ -427,8 +434,8 @@
/**
* If there is a ringing incoming call, this method accepts the call on behalf of the user.
- * TODO(santoscordon): L-release - need to convert all invocation of
- * ITelecommService#answerRingingCall to use this method (clockwork & gearhead).
+ * TODO: L-release - need to convert all invocation of ITelecommService#answerRingingCall to use
+ * this method (clockwork & gearhead).
*
* @hide
*/
@@ -478,12 +485,12 @@
/**
* Returns the current TTY mode of the device. For TTY to be on the user must enable it in
- * settings and have a wired headset plugged in. Valid modes are:
+ * settings and have a wired headset plugged in.
+ * Valid modes are:
* - {@link TelecommManager#TTY_MODE_OFF}
* - {@link TelecommManager#TTY_MODE_FULL}
* - {@link TelecommManager#TTY_MODE_HCO}
* - {@link TelecommManager#TTY_MODE_VCO}
- *
* @hide
*/
public int getCurrentTtyMode() {
@@ -497,6 +504,31 @@
return TTY_MODE_OFF;
}
+ /**
+ * Registers a new incoming call. A {@link ConnectionService} should invoke this method when it
+ * has an incoming call. The specified {@link PhoneAccountHandle} must have been registered
+ * with {@link #registerPhoneAccount} and subsequently enabled by the user within the phone's
+ * settings. Once invoked, this method will cause the system to bind to the
+ * {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request
+ * additional information about the call (See
+ * {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming call UI.
+ *
+ * @param phoneAccount A {@link PhoneAccountHandle} registered with
+ * {@link #registerPhoneAccount}.
+ * @param extras A bundle that will be passed through to
+ * {@link ConnectionService#onCreateIncomingConnection}.
+ */
+ public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
+ try {
+ if (isServiceConnected()) {
+ getTelecommService().addNewIncomingCall(
+ phoneAccount, extras == null ? new Bundle() : extras);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException adding a new incoming call: " + phoneAccount, e);
+ }
+ }
+
private ITelecommService getTelecommService() {
return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
}
diff --git a/telecomm/java/android/telecomm/VideoCallProfile.java b/telecomm/java/android/telecomm/VideoCallProfile.java
index fc7b2c3..5b15e4a 100644
--- a/telecomm/java/android/telecomm/VideoCallProfile.java
+++ b/telecomm/java/android/telecomm/VideoCallProfile.java
@@ -77,6 +77,15 @@
* Creates an instance of the VideoCallProfile
*
* @param videoState The video state.
+ */
+ public VideoCallProfile(int videoState) {
+ this(videoState, QUALITY_DEFAULT);
+ }
+
+ /**
+ * Creates an instance of the VideoCallProfile
+ *
+ * @param videoState The video state.
* @param quality The video quality.
*/
public VideoCallProfile(int videoState, int quality) {
diff --git a/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
index 1abdf7c..552993f 100644
--- a/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
@@ -19,6 +19,7 @@
import android.app.PendingIntent;
import android.net.Uri;
import android.telecomm.ConnectionRequest;
+import android.telecomm.ParcelableConnection;
import android.telecomm.StatusHints;
import com.android.internal.telecomm.ICallVideoProvider;
@@ -32,7 +33,8 @@
* {@hide}
*/
oneway interface IConnectionServiceAdapter {
- void handleCreateConnectionSuccessful(in ConnectionRequest request);
+ void handleCreateConnectionSuccessful(
+ in ConnectionRequest request, in ParcelableConnection connection);
void handleCreateConnectionFailed(
in ConnectionRequest request, int errorCode, String errorMessage);
diff --git a/telecomm/java/com/android/internal/telecomm/IInCallService.aidl b/telecomm/java/com/android/internal/telecomm/IInCallService.aidl
index fc28ed5..8ec6196 100644
--- a/telecomm/java/com/android/internal/telecomm/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IInCallService.aidl
@@ -18,7 +18,7 @@
import android.app.PendingIntent;
import android.telecomm.CallAudioState;
-import android.telecomm.InCallCall;
+import android.telecomm.ParcelableCall;
import com.android.internal.telecomm.IInCallAdapter;
@@ -32,9 +32,9 @@
oneway interface IInCallService {
void setInCallAdapter(in IInCallAdapter inCallAdapter);
- void addCall(in InCallCall call);
+ void addCall(in ParcelableCall call);
- void updateCall(in InCallCall call);
+ void updateCall(in ParcelableCall call);
void setPostDial(String callId, String remaining);
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index 1536442..5036948 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.telecomm.PhoneAccountHandle;
+import android.os.Bundle;
import android.telecomm.PhoneAccount;
/**
@@ -116,4 +117,9 @@
* @see TelecomManager#getCurrentTtyMode
*/
int getCurrentTtyMode();
+
+ /**
+ * @see TelecommManager#addNewIncomingCall
+ */
+ void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 9cff765..35568cf 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -20,6 +20,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -2788,13 +2789,13 @@
/**
* Get P-CSCF address from PCO after data connection is established or modified.
- *
+ * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
* @return array of P-CSCF address
* @hide
*/
- public String[] getPcscfAddress() {
+ public String[] getPcscfAddress(String apnType) {
try {
- return getITelephony().getPcscfAddress();
+ return getITelephony().getPcscfAddress(apnType);
} catch (RemoteException e) {
return new String[0];
}
@@ -2980,6 +2981,19 @@
/** @hide */
@SystemApi
+ public List<String> getCarrierPackageNamesForBroadcastIntent(Intent intent) {
+ try {
+ return getITelephony().getCarrierPackageNamesForBroadcastIntent(intent);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getCarrierPackageNamesForBroadcastIntent RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getCarrierPackageNamesForBroadcastIntent NPE", ex);
+ }
+ return null;
+ }
+
+ /** @hide */
+ @SystemApi
public void dial(String number) {
try {
getITelephony().dial(number);
@@ -3308,4 +3322,23 @@
}
return false;
}
+
+ /**
+ * Returns the result and response from RIL for oem request
+ *
+ * @param oemReq the data is sent to ril.
+ * @param oemResp the respose data from RIL.
+ * @return negative value request was not handled or get error
+ * 0 request was handled succesfully, but no response data
+ * positive value success, data length of response
+ * @hide
+ */
+ public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
+ try {
+ return getITelephony().invokeOemRilRequestRaw(oemReq, oemResp);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return -1;
+ }
}
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
index 5f243a0..1413e58 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -55,4 +55,15 @@
* Else ({@code event} is 1), meaning the specified service is added to the IMS connection.
*/
void registrationServiceCapabilityChanged(int serviceClass, int event);
+
+ /**
+ * Notifies the application when features on a particular service enabled or
+ * disabled successfully based on user preferences.
+ *
+ * @param serviceClass a service class specified in {@link ImsServiceClass}
+ * @param enabledFeatures features enabled as defined in com.android.ims.ImsConfig#FeatureConstants.
+ * @param disabledFeatures features disabled as defined in com.android.ims.ImsConfig#FeatureConstants.
+ */
+ void registrationFeatureCapabilityChanged(int serviceClass,
+ out int[] enabledFeatures, out int[] disabledFeatures);
}
diff --git a/telephony/java/com/android/ims/internal/IImsService.aidl b/telephony/java/com/android/ims/internal/IImsService.aidl
index d992124..869cd9f 100644
--- a/telephony/java/com/android/ims/internal/IImsService.aidl
+++ b/telephony/java/com/android/ims/internal/IImsService.aidl
@@ -51,4 +51,15 @@
* Config interface to get/set IMS service/capability parameters.
*/
IImsConfig getConfigInterface();
+
+ /**
+ * Used for turning on IMS when its in OFF state.
+ */
+ void turnOnIms();
+
+ /**
+ * Used for turning off IMS when its in ON state.
+ * When IMS is OFF, device will behave as CSFB'ed.
+ */
+ void turnOffIms();
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 435c334..886de40 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.content.Intent;
import android.os.Bundle;
import java.util.List;
import android.telephony.NeighboringCellInfo;
@@ -633,8 +634,9 @@
/**
* Get P-CSCF address from PCO after data connection is established or modified.
+ * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
*/
- String[] getPcscfAddress();
+ String[] getPcscfAddress(String apnType);
/**
* Set IMS registration state
@@ -660,6 +662,17 @@
int checkCarrierPrivilegesForPackage(String pkgname);
/**
+ * Returns the package name of the carrier apps that should handle the input intent.
+ *
+ * @param packageManager PackageManager for getting receivers.
+ * @param intent Intent that will be broadcast.
+ * @return list of carrier app package names that can handle the intent.
+ * Returns null if there is an error and an empty list if there
+ * are no matching packages.
+ */
+ List<String> getCarrierPackageNamesForBroadcastIntent(in Intent intent);
+
+ /**
* Set whether Android should display a simplified Mobile Network Settings UI.
* The setting won't be persisted during power cycle.
*
@@ -724,5 +737,16 @@
* @return true if the operation was executed correctly.
*/
boolean setOperatorBrandOverride(String iccId, String brand);
+
+ /**
+ * Returns the result and response from RIL for oem request
+ *
+ * @param oemReq the data is sent to ril.
+ * @param oemResp the respose data from RIL.
+ * @return negative value request was not handled or get error
+ * 0 request was handled succesfully, but no response data
+ * positive value success, data length of response
+ */
+ int invokeOemRilRequestRaw(in byte[] oemReq, out byte[] oemResp);
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index e388480..36c90f2 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -661,7 +661,7 @@
* @hide
*/
@Override
- public boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked,
+ public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
UserHandle user) {
return false;
}
@@ -670,7 +670,7 @@
* @hide
*/
@Override
- public boolean getApplicationBlockedSettingAsUser(String packageName, UserHandle user) {
+ public boolean getApplicationHiddenSettingAsUser(String packageName, UserHandle user) {
return false;
}
diff --git a/tests/DozeTest/AndroidManifest.xml b/tests/DozeTest/AndroidManifest.xml
index c199f69..03778d6 100644
--- a/tests/DozeTest/AndroidManifest.xml
+++ b/tests/DozeTest/AndroidManifest.xml
@@ -22,7 +22,8 @@
android:name="DozeTestDream"
android:exported="true"
android:icon="@drawable/ic_app"
- android:label="@string/doze_dream_name">
+ android:label="@string/doze_dream_name"
+ android:permission="android.permission.BIND_DREAM_SERVICE">
<!-- Commented out to prevent this dream from appearing in the list of
dreams that the user can select via the Settings application.
<intent-filter>
diff --git a/tests/DozeTest/res/layout/dream.xml b/tests/DozeTest/res/layout/dream.xml
index 1c8fd3f..bced230 100644
--- a/tests/DozeTest/res/layout/dream.xml
+++ b/tests/DozeTest/res/layout/dream.xml
@@ -18,7 +18,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:background="#bb2288">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
index a0b2d1a..f72e331 100644
--- a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
+++ b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
@@ -22,11 +22,13 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Handler;
import android.os.PowerManager;
import android.service.dreams.DozeHardware;
import android.service.dreams.DreamService;
import android.text.format.DateFormat;
import android.util.Log;
+import android.view.Display;
import android.widget.TextView;
import java.util.Date;
@@ -51,10 +53,16 @@
// Doesn't mean anything. Real hardware won't handle it.
private static final String TEST_PING_MESSAGE = "test.ping";
+ // Not all hardware supports dozing. We should use Display.STATE_DOZE but
+ // for testing purposes it is convenient to use Display.STATE_ON so the
+ // test still works on hardware that does not support dozing.
+ private static final int DISPLAY_STATE_WHEN_DOZING = Display.STATE_ON;
+
private PowerManager mPowerManager;
private PowerManager.WakeLock mWakeLock;
private AlarmManager mAlarmManager;
private PendingIntent mAlarmIntent;
+ private Handler mHandler = new Handler();
private TextView mAlarmClock;
@@ -64,6 +72,8 @@
private boolean mDreaming;
private DozeHardware mDozeHardware;
+ private long mLastTime = Long.MIN_VALUE;
+
@Override
public void onCreate() {
super.onCreate();
@@ -80,6 +90,8 @@
registerReceiver(mAlarmReceiver, filter);
mAlarmIntent = PendingIntent.getBroadcast(this, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
+
+ setDozeScreenState(DISPLAY_STATE_WHEN_DOZING);
}
@Override
@@ -143,13 +155,33 @@
if (mDreaming) {
long now = System.currentTimeMillis();
now -= now % 60000; // back up to last minute boundary
+ if (mLastTime == now) {
+ return;
+ }
+ mLastTime = now;
mTime.setTime(now);
mAlarmClock.setText(mTimeFormat.format(mTime));
mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, now + 60000, mAlarmIntent);
- mWakeLock.acquire(UPDATE_TIME_TIMEOUT);
+ mWakeLock.acquire(UPDATE_TIME_TIMEOUT + 5000 /*for testing brightness*/);
+
+ // flash the screen a bit to test these functions
+ setDozeScreenState(DISPLAY_STATE_WHEN_DOZING);
+ setDozeScreenBrightness(200);
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ setDozeScreenBrightness(50);
+ }
+ }, 2000);
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ setDozeScreenState(Display.STATE_OFF);
+ }
+ }, 5000);
}
}
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
index 397ef13..051ed0e 100644
--- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -34,8 +34,10 @@
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* This test is intended to measure the amount of memory applications use when
@@ -57,11 +59,12 @@
private static final String TAG = "MemoryUsageInstrumentation";
private static final String KEY_APPS = "apps";
-
+ private static final String KEY_PROCS = "persistent";
+ private static final String LAUNCHER_KEY = "launcher";
private Map<String, Intent> mNameToIntent;
private Map<String, String> mNameToProcess;
private Map<String, String> mNameToResultKey;
-
+ private Set<String> mPersistentProcesses;
private IActivityManager mAm;
public void testMemory() {
@@ -75,35 +78,61 @@
Bundle results = new Bundle();
for (String app : mNameToResultKey.keySet()) {
- String processName;
- try {
- processName = startApp(app);
- measureMemory(app, processName, results);
- closeApp();
- } catch (NameNotFoundException e) {
- Log.i(TAG, "Application " + app + " not found");
+ if (!mPersistentProcesses.contains(app)) {
+ String processName;
+ try {
+ processName = startApp(app);
+ measureMemory(app, processName, results);
+ closeApp();
+ } catch (NameNotFoundException e) {
+ Log.i(TAG, "Application " + app + " not found");
+ }
+ } else {
+ measureMemory(app, app, results);
}
-
}
instrumentation.sendStatus(0, results);
}
- private void parseArgs(Bundle args) {
- mNameToResultKey = new HashMap<String, String>();
- String appList = args.getString(KEY_APPS);
+ private String getLauncherPackageName() {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ ResolveInfo resolveInfo = getInstrumentation().getContext().
+ getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ return resolveInfo.activityInfo.packageName;
+ }
- if (appList == null)
- return;
-
- String appNames[] = appList.split("\\|");
- for (String pair : appNames) {
+ private Map<String, String> parseListToMap(String list) {
+ Map<String, String> map = new HashMap<String, String>();
+ String names[] = list.split("\\|");
+ for (String pair : names) {
String[] parts = pair.split("\\^");
if (parts.length != 2) {
Log.e(TAG, "The apps key is incorectly formatted");
fail();
}
+ map.put(parts[0], parts[1]);
+ }
+ return map;
+ }
- mNameToResultKey.put(parts[0], parts[1]);
+ private void parseArgs(Bundle args) {
+ mNameToResultKey = new HashMap<String, String>();
+ mPersistentProcesses = new HashSet<String>();
+ String appList = args.getString(KEY_APPS);
+ String procList = args.getString(KEY_PROCS);
+ String mLauncherPackageName = getLauncherPackageName();
+ mPersistentProcesses.add(mLauncherPackageName);
+ mNameToResultKey.put(mLauncherPackageName, LAUNCHER_KEY);
+ if (appList == null && procList == null)
+ return;
+ if (appList != null) {
+ mNameToResultKey.putAll(parseListToMap(appList));
+ }
+ if (procList != null) {
+ Map<String, String> procMap = parseListToMap(procList);
+ mPersistentProcesses.addAll(procMap.keySet());
+ mNameToResultKey.putAll(procMap);
}
}
diff --git a/tests/MusicBrowserDemo/Android.mk b/tests/MusicBrowserDemo/Android.mk
new file mode 100644
index 0000000..207774b
--- /dev/null
+++ b/tests/MusicBrowserDemo/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := MusicBrowserDemo
+#LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-v4 \
+ android-support-v7-appcompat
+
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/res \
+ frameworks/support/v7/appcompat/res
+LOCAL_PROGUARD_ENABLED := disabled
+#LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+LOCAL_AAPT_FLAGS := \
+ --auto-add-overlay \
+ --extra-packages android.support.v7.appcompat
+include $(BUILD_PACKAGE)
diff --git a/tests/MusicBrowserDemo/AndroidManifest.xml b/tests/MusicBrowserDemo/AndroidManifest.xml
new file mode 100644
index 0000000..d2acfe2
--- /dev/null
+++ b/tests/MusicBrowserDemo/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.musicbrowserdemo"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="9"
+ android:targetSdkVersion="19" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme"
+ >
+
+ <activity
+ android:name="com.example.android.musicbrowserdemo.MainActivity"
+ android:label="@string/app_name"
+ >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ </application>
+
+</manifest>
diff --git a/tests/MusicBrowserDemo/res/drawable-hdpi/ic_launcher.png b/tests/MusicBrowserDemo/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..47d6854
--- /dev/null
+++ b/tests/MusicBrowserDemo/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicBrowserDemo/res/drawable-mdpi/ic_launcher.png b/tests/MusicBrowserDemo/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..01b53fd
--- /dev/null
+++ b/tests/MusicBrowserDemo/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicBrowserDemo/res/drawable-xhdpi/ic_launcher.png b/tests/MusicBrowserDemo/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..af762f2
--- /dev/null
+++ b/tests/MusicBrowserDemo/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicBrowserDemo/res/drawable-xxhdpi/ic_launcher.png b/tests/MusicBrowserDemo/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..eef47aa
--- /dev/null
+++ b/tests/MusicBrowserDemo/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicBrowserDemo/res/values/strings.xml b/tests/MusicBrowserDemo/res/values/strings.xml
new file mode 100644
index 0000000..858f278f
--- /dev/null
+++ b/tests/MusicBrowserDemo/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+
+ <string name="app_name">Music Browser</string>
+
+</resources>
diff --git a/tests/MusicBrowserDemo/res/values/styles.xml b/tests/MusicBrowserDemo/res/values/styles.xml
new file mode 100644
index 0000000..b83662d
--- /dev/null
+++ b/tests/MusicBrowserDemo/res/values/styles.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
new file mode 100644
index 0000000..c0f3a7f
--- /dev/null
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+package com.example.android.musicbrowserdemo;
+
+import android.content.Context;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.media.browse.MediaBrowserService;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.app.ListFragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// TODO: Include an icon.
+
+public class AppListFragment extends ListFragment {
+
+ private Adapter mAdapter;
+ private List<Item> mItems;
+
+ public AppListFragment() {
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ mAdapter = new Adapter();
+ setListAdapter(mAdapter);
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ final Item item = mItems.get(position);
+
+ Log.i("AppListFragment", "Item clicked: " + position + " -- " + item.component);
+
+ final BrowserListFragment fragment = new BrowserListFragment();
+
+ final Bundle args = new Bundle();
+ args.putParcelable(BrowserListFragment.ARG_COMPONENT, item.component);
+ fragment.setArguments(args);
+
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, fragment)
+ .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
+ .addToBackStack(null)
+ .commit();
+ }
+
+ private static class Item {
+ final String label;
+ final ComponentName component;
+
+ Item(String l, ComponentName c) {
+ this.label = l;
+ this.component = c;
+ }
+ }
+
+ private class Adapter extends BaseAdapter {
+ private final LayoutInflater mInflater;
+
+ Adapter() {
+ super();
+
+ final Context context = getActivity();
+ mInflater = LayoutInflater.from(context);
+
+ // Load the data
+ final PackageManager pm = context.getPackageManager();
+ final Intent intent = new Intent(MediaBrowserService.SERVICE_ACTION);
+ final List<ResolveInfo> list = pm.queryIntentServices(intent, 0);
+ final int N = list.size();
+ mItems = new ArrayList(N);
+ for (int i=0; i<N; i++) {
+ final ResolveInfo ri = list.get(i);
+ mItems.add(new Item(ri.loadLabel(pm).toString(), new ComponentName(
+ ri.serviceInfo.applicationInfo.packageName,
+ ri.serviceInfo.name)));
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return mItems.size();
+ }
+
+ @Override
+ public Item getItem(int position) {
+ return mItems.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return 1;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
+ }
+
+ final TextView tv = (TextView)convertView;
+ final Item item = mItems.get(position);
+ tv.setText(item.label);
+
+ return convertView;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
+ }
+}
+
+
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
new file mode 100644
index 0000000..3fc468d
--- /dev/null
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+package com.example.android.musicbrowserdemo;
+
+import android.content.Context;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowserItem;
+import android.media.browse.MediaBrowserService;
+import android.os.Bundle;
+import android.net.Uri;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.app.ListFragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BrowserListFragment extends ListFragment {
+ private static final String TAG = "BrowserListFragment";
+
+ // Hints
+ public static final String HINT_DISPLAY = "com.example.android.musicbrowserdemo.DISPLAY";
+
+ // For args
+ public static final String ARG_COMPONENT = "component";
+ public static final String ARG_URI = "uri";
+
+ private Adapter mAdapter;
+ private List<Item> mItems = new ArrayList();
+ private ComponentName mComponent;
+ private Uri mUri;
+ private MediaBrowser mBrowser;
+
+ private static class Item {
+ final MediaBrowserItem media;
+
+ Item(MediaBrowserItem m) {
+ this.media = m;
+ }
+ }
+
+ public BrowserListFragment() {
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ Log.d(TAG, "onActivityCreated -- " + hashCode());
+ mAdapter = new Adapter();
+ setListAdapter(mAdapter);
+
+ // Get our arguments
+ final Bundle args = getArguments();
+ mComponent = args.getParcelable(ARG_COMPONENT);
+ mUri = args.getParcelable(ARG_URI);
+
+ // A hint about who we are, so the service can customize the results if it wants to.
+ final Bundle rootHints = new Bundle();
+ rootHints.putBoolean(HINT_DISPLAY, true);
+
+ mBrowser = new MediaBrowser(getActivity(), mComponent, mConnectionCallbacks, rootHints);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mBrowser.connect();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ mBrowser.disconnect();
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ final Item item = mItems.get(position);
+
+ Log.i("BrowserListFragment", "Item clicked: " + position + " -- "
+ + mAdapter.getItem(position).media.getUri());
+
+ final BrowserListFragment fragment = new BrowserListFragment();
+
+ final Bundle args = new Bundle();
+ args.putParcelable(BrowserListFragment.ARG_COMPONENT, mComponent);
+ args.putParcelable(BrowserListFragment.ARG_URI, item.media.getUri());
+ fragment.setArguments(args);
+
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, fragment)
+ .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
+ .addToBackStack(null)
+ .commit();
+
+ }
+
+ final MediaBrowser.ConnectionCallback mConnectionCallbacks
+ = new MediaBrowser.ConnectionCallback() {
+ @Override
+ public void onConnected() {
+ Log.d(TAG, "mConnectionCallbacks.onConnected");
+ if (mUri == null) {
+ mUri = mBrowser.getRoot();
+ }
+ mBrowser.subscribe(mUri, new MediaBrowser.SubscriptionCallback() {
+ @Override
+ public void onChildrenLoaded(Uri parentUri, List<MediaBrowserItem> children) {
+ Log.d(TAG, "onChildrenLoaded parentUri=" + parentUri
+ + " children= " + children);
+ mItems.clear();
+ final int N = children.size();
+ for (int i=0; i<N; i++) {
+ mItems.add(new Item(children.get(i)));
+ }
+ mAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onError(Uri parentUri) {
+ Log.d(TAG, "onError parentUri=" + parentUri);
+ }
+ });
+ }
+
+ @Override
+ public void onConnectionSuspended() {
+ Log.d(TAG, "mConnectionCallbacks.onConnectionSuspended");
+ }
+
+ @Override
+ public void onConnectionFailed() {
+ Log.d(TAG, "mConnectionCallbacks.onConnectionFailed");
+ }
+ };
+
+ private class Adapter extends BaseAdapter {
+ private final LayoutInflater mInflater;
+
+ Adapter() {
+ super();
+
+ final Context context = getActivity();
+ mInflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public int getCount() {
+ return mItems.size();
+ }
+
+ @Override
+ public Item getItem(int position) {
+ return mItems.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return 1;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
+ }
+
+ final TextView tv = (TextView)convertView;
+ final Item item = mItems.get(position);
+ tv.setText(item.media.getTitle());
+
+ return convertView;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
+ }
+}
+
+
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java
new file mode 100644
index 0000000..ed91aad
--- /dev/null
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package com.example.android.musicbrowserdemo;
+
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+ * Main activity class.
+ */
+public class MainActivity extends FragmentActivity {
+
+ private static final String BROWSER_FRAGMENT_TAG = "browser";
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ Log.d("MainActivity", "-------------------------------------------------------");
+
+ // If we are starting afresh, start at the app list.
+ final FragmentManager fm = getSupportFragmentManager();
+ if (fm.findFragmentById(android.R.id.content) == null) {
+ fm.beginTransaction().add(android.R.id.content, new AppListFragment()).commit();
+ }
+ }
+}
+
diff --git a/tests/MusicServiceDemo/Android.mk b/tests/MusicServiceDemo/Android.mk
new file mode 100644
index 0000000..feef67a
--- /dev/null
+++ b/tests/MusicServiceDemo/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := MusicServiceDemo
+#LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-v4 \
+ android-support-v7-appcompat
+
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/res \
+ frameworks/support/v7/appcompat/res
+LOCAL_PROGUARD_ENABLED := disabled
+#LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+LOCAL_AAPT_FLAGS := \
+ --auto-add-overlay \
+ --extra-packages android.support.v7.appcompat
+include $(BUILD_PACKAGE)
diff --git a/tests/MusicServiceDemo/AndroidManifest.xml b/tests/MusicServiceDemo/AndroidManifest.xml
new file mode 100644
index 0000000..e00e2e2
--- /dev/null
+++ b/tests/MusicServiceDemo/AndroidManifest.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.musicservicedemo"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+ <uses-sdk
+ android:minSdkVersion="9"
+ android:targetSdkVersion="19" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme"
+ >
+
+ <activity
+ android:name="com.example.android.automotive.musicplayer.MainActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <service
+ android:name=".BrowserService"
+ android:exported="true"
+ >
+ <intent-filter>
+ <action android:name="android.media.browse.MediaBrowserService" />
+ </intent-filter>
+ </service>
+ </application>
+
+</manifest>
diff --git a/tests/MusicServiceDemo/proguard-project.txt b/tests/MusicServiceDemo/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/tests/MusicServiceDemo/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/tests/MusicServiceDemo/res/drawable-hdpi/ic_launcher.png b/tests/MusicServiceDemo/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..47d6854
--- /dev/null
+++ b/tests/MusicServiceDemo/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicServiceDemo/res/drawable-mdpi/ic_launcher.png b/tests/MusicServiceDemo/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..01b53fd
--- /dev/null
+++ b/tests/MusicServiceDemo/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicServiceDemo/res/drawable-xhdpi/ic_launcher.png b/tests/MusicServiceDemo/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..af762f2
--- /dev/null
+++ b/tests/MusicServiceDemo/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicServiceDemo/res/drawable-xxhdpi/ic_launcher.png b/tests/MusicServiceDemo/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..eef47aa
--- /dev/null
+++ b/tests/MusicServiceDemo/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicServiceDemo/res/drawable-xxhdpi/thumbsup.png b/tests/MusicServiceDemo/res/drawable-xxhdpi/thumbsup.png
new file mode 100644
index 0000000..ea98c95
--- /dev/null
+++ b/tests/MusicServiceDemo/res/drawable-xxhdpi/thumbsup.png
Binary files differ
diff --git a/tests/MusicServiceDemo/res/layout/activity_main.xml b/tests/MusicServiceDemo/res/layout/activity_main.xml
new file mode 100644
index 0000000..71753e3
--- /dev/null
+++ b/tests/MusicServiceDemo/res/layout/activity_main.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="com.example.android.automotive.musicplayer.MainActivity"
+ tools:ignore="MergeRootFrame" />
diff --git a/tests/MusicServiceDemo/res/layout/fragment_main.xml b/tests/MusicServiceDemo/res/layout/fragment_main.xml
new file mode 100644
index 0000000..8796e86
--- /dev/null
+++ b/tests/MusicServiceDemo/res/layout/fragment_main.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ tools:context="com.example.android.automotive.musicplayer.MainActivity$PlaceholderFragment" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/app_name" />
+
+</RelativeLayout>
diff --git a/tests/MusicServiceDemo/res/values/colors.xml b/tests/MusicServiceDemo/res/values/colors.xml
new file mode 100644
index 0000000..44dd05d
--- /dev/null
+++ b/tests/MusicServiceDemo/res/values/colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <color name="yellow">#ffffff00</color>
+ <color name="green">#ff00ff00</color>
+ <color name="blue">#ff0000ff</color>
+ <color name="red">#ffff0000</color>
+</resources>
diff --git a/tests/MusicServiceDemo/res/values/dimens.xml b/tests/MusicServiceDemo/res/values/dimens.xml
new file mode 100644
index 0000000..9f63ef2
--- /dev/null
+++ b/tests/MusicServiceDemo/res/values/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
diff --git a/tests/MusicServiceDemo/res/values/strings.xml b/tests/MusicServiceDemo/res/values/strings.xml
new file mode 100644
index 0000000..14c0171
--- /dev/null
+++ b/tests/MusicServiceDemo/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+
+ <string name="app_name">Music Service Demo</string>
+ <string name="action_settings">Settings</string>
+ <string name="thumbs_up">Thumbs Up</string>
+ <string name="music_error">No music found</string>
+ <string name="now_playing">Now Playing</string>
+
+</resources>
diff --git a/tests/MusicServiceDemo/res/values/styles.xml b/tests/MusicServiceDemo/res/values/styles.xml
new file mode 100644
index 0000000..b83662d
--- /dev/null
+++ b/tests/MusicServiceDemo/res/values/styles.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java
new file mode 100644
index 0000000..0e7fe13
--- /dev/null
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.musicservicedemo;
+
+import android.app.SearchManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.UriMatcher;
+import android.content.res.Resources.NotFoundException;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.media.browse.MediaBrowserItem;
+import android.media.browse.MediaBrowserService;
+import android.media.browse.MediaBrowserService.BrowserRoot;
+import android.media.session.MediaSession;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.WifiLock;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.example.android.musicservicedemo.browser.MusicProvider;
+import com.example.android.musicservicedemo.browser.MusicProviderTask;
+import com.example.android.musicservicedemo.browser.MusicProviderTaskListener;
+import com.example.android.musicservicedemo.browser.MusicTrack;
+
+import org.json.JSONException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Service that implements MediaBrowserService and returns our menu hierarchy.
+ */
+public class BrowserService extends MediaBrowserService {
+ private static final String TAG = "BrowserService";
+
+ // URI paths for browsing music
+ public static final String BROWSE_ROOT_BASE_PATH = "browse";
+ public static final String NOW_PLAYING_PATH = "now_playing";
+ public static final String PIANO_BASE_PATH = "piano";
+ public static final String VOICE_BASE_PATH = "voice";
+
+ // Content URIs
+ public static final String AUTHORITY = "com.example.android.automotive.musicplayer";
+ public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY);
+ public static final Uri BROWSE_URI = Uri.withAppendedPath(BASE_URI, BROWSE_ROOT_BASE_PATH);
+
+ // URI matcher constants for browsing paths
+ public static final int BROWSE_ROOT = 1;
+ public static final int NOW_PLAYING = 2;
+ public static final int PIANO = 3;
+ public static final int VOICE = 4;
+
+ // Map the the URI paths with the URI matcher constants
+ private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ static {
+ sUriMatcher.addURI(AUTHORITY, BROWSE_ROOT_BASE_PATH, BROWSE_ROOT);
+ sUriMatcher.addURI(AUTHORITY, NOW_PLAYING_PATH, NOW_PLAYING);
+ sUriMatcher.addURI(AUTHORITY, PIANO_BASE_PATH, PIANO);
+ sUriMatcher.addURI(AUTHORITY, VOICE_BASE_PATH, VOICE);
+ }
+
+ // Media metadata that will be provided for a media container
+ public static final String[] MEDIA_CONTAINER_PROJECTION = {
+ "uri",
+ "title",
+ "subtitle",
+ "image_uri",
+ "supported_actions"
+ };
+
+ // MusicProvider will download the music catalog
+ private MusicProvider mMusicProvider;
+
+ private MediaSession mSession;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ mSession = new MediaSession(this, "com.example.android.musicservicedemo.BrowserService");
+ setSessionToken(mSession.getSessionToken());
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ protected BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+ return new BrowserRoot(BROWSE_URI, null);
+ }
+
+ @Override
+ protected void onLoadChildren(final Uri parentUri,
+ final Result<List<MediaBrowserItem>> result) {
+ new Handler().postDelayed(new Runnable() {
+ public void run() {
+ final ArrayList<MediaBrowserItem> list = new ArrayList();
+
+ for (int i=0; i<10; i++) {
+ list.add(new MediaBrowserItem.Builder(
+ Uri.withAppendedPath(BASE_URI, Integer.toString(i)),
+ MediaBrowserItem.FLAG_BROWSABLE, "Title " + i)
+ .setSummary("Summary " + i)
+ .build());
+ }
+
+ result.sendResult(list);
+ }
+ }, 2000);
+ result.detach();
+ }
+
+ @Override
+ protected void onLoadThumbnail(Uri uri, int width, int height, Result<Bitmap> result) {
+ result.sendResult(null);
+ }
+
+ /*
+ @Override
+ public void query(final Query query, final IMetadataResultHandler metadataResultHandler,
+ final IErrorHandler errorHandler)
+ throws RemoteException {
+ Log.d(TAG, "query: " + query);
+ Utils.checkNotNull(query);
+ Utils.checkNotNull(metadataResultHandler);
+ Utils.checkNotNull(errorHandler);
+
+ // Handle async response
+ new Thread(new Runnable() {
+ public void run() {
+ try {
+ // Pre-load the list of music
+ List<MusicTrack> musicTracks = getMusicList();
+ if (musicTracks == null) {
+ notifyListenersOnPlaybackStateUpdate(getCurrentPlaybackState());
+ errorHandler.onError(new Error(Error.UNKNOWN,
+ getString(R.string.music_error)));
+ return;
+ }
+
+ final Uri uri = query.getUri();
+ int match = sUriMatcher.match(uri);
+ Log.d(TAG, "Queried: " + uri + "; match: " + match);
+ switch (match) {
+ case BROWSE_ROOT:
+ {
+ Log.d(TAG, "Browse_root");
+
+ try {
+ MatrixCursor matrixCursor = mMusicProvider
+ .getRootContainerCurser();
+ DataHolder holder = new DataHolder(MEDIA_CONTAINER_PROJECTION,
+ matrixCursor, null);
+
+ Log.d(TAG, "on metadata response called " + holder.getCount());
+ metadataResultHandler.onMetadataResponse(holder);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error delivering metadata in the callback.", e);
+ }
+ break;
+ }
+ case NOW_PLAYING:
+ {
+ try {
+ Log.d(TAG, "query NOW_PLAYING");
+ MatrixCursor matrixCursor = mMusicProvider
+ .getRootItemCursor(
+ PIANO);
+ DataHolder holder = new DataHolder(MEDIA_CONTAINER_PROJECTION,
+ matrixCursor, null);
+ Log.d(TAG, "on metadata response called " + holder.getCount());
+ metadataResultHandler.onMetadataResponse(holder);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error querying NOW_PLAYING");
+ }
+ break;
+ }
+ case PIANO:
+ {
+ try {
+ Log.d(TAG, "query PIANO");
+ MatrixCursor matrixCursor = mMusicProvider
+ .getRootItemCursor(
+ PIANO);
+ DataHolder holder = new DataHolder(MEDIA_CONTAINER_PROJECTION,
+ matrixCursor, null);
+ Log.d(TAG, "on metadata response called " + holder.getCount());
+ metadataResultHandler.onMetadataResponse(holder);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error querying PIANO");
+ }
+ break;
+ }
+ case VOICE:
+ {
+ try {
+ Log.d(TAG, "query VOICE");
+ MatrixCursor matrixCursor = mMusicProvider
+ .getRootItemCursor(
+ VOICE);
+ DataHolder holder = new DataHolder(MEDIA_CONTAINER_PROJECTION,
+ matrixCursor, null);
+ Log.d(TAG, "on metadata response called " + holder.getCount());
+ metadataResultHandler.onMetadataResponse(holder);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error querying VOICE");
+ }
+ break;
+ }
+ default:
+ {
+ Log.w(TAG, "Skipping unmatched URI: " + uri);
+ }
+ }
+ } catch (NotFoundException e) {
+ Log.e(TAG, "::run:", e);
+ } catch (RemoteException e) {
+ Log.e(TAG, "::run:", e);
+ }
+ } // end run
+ }).start();
+ }
+
+ */
+}
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/MainActivity.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/MainActivity.java
new file mode 100644
index 0000000..db45b9d
--- /dev/null
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/MainActivity.java
@@ -0,0 +1,101 @@
+/* Copyright 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.musicservicedemo;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.ActionBarActivity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.example.android.musicservicedemo.R;
+
+// TODO Local UI
+
+/**
+ * Main activity of the app.
+ */
+public class MainActivity extends ActionBarActivity {
+
+ private static final String LOG = "MainActivity";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ if (savedInstanceState == null) {
+ getSupportFragmentManager().beginTransaction()
+ .add(R.id.container, new PlaceholderFragment())
+ .commit();
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
+ */
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+
+ // Inflate the menu; this adds items to the action bar if it is present.
+ //getMenuInflater().inflate(R.menu.main, menu);
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+ // if (id == R.id.action_settings) {
+ // return true;
+ // }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * A placeholder fragment containing a simple view.
+ */
+ public static class PlaceholderFragment extends Fragment {
+
+ public PlaceholderFragment() {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater
+ * , android.view.ViewGroup, android.os.Bundle)
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.fragment_main, container, false);
+ return rootView;
+ }
+ }
+
+}
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/Utils.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/Utils.java
new file mode 100644
index 0000000..3589761
--- /dev/null
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/Utils.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.musicservicedemo;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+public class Utils {
+
+ private static final String TAG = "Utils";
+
+ /**
+ * Utility method to check that parameters are not null
+ *
+ * @param object
+ */
+ public static final void checkNotNull(Object object) {
+ if (object == null) {
+ throw new NullPointerException();
+ }
+ }
+
+ /**
+ * Utility to download a bitmap
+ *
+ * @param source
+ * @return
+ */
+ public static Bitmap getBitmapFromURL(String source) {
+ try {
+ URL url = new URL(source);
+ HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
+ httpConnection.setDoInput(true);
+ httpConnection.connect();
+ InputStream inputStream = httpConnection.getInputStream();
+ return BitmapFactory.decodeStream(inputStream);
+ } catch (IOException e) {
+ Log.e(TAG, "getBitmapFromUrl: " + source, e);
+ }
+ return null;
+ }
+
+ /**
+ * Utility method to wrap an index
+ *
+ * @param i
+ * @param size
+ * @return
+ */
+ public static int wrapIndex(int i, int size) {
+ int m = i % size;
+ if (m < 0) { // java modulus can be negative
+ m += size;
+ }
+ return m;
+ }
+}
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProvider.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProvider.java
new file mode 100644
index 0000000..15038d7
--- /dev/null
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProvider.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.musicservicedemo.browser;
+
+import android.database.MatrixCursor;
+import android.media.session.PlaybackState;
+import android.net.Uri;
+import android.util.Log;
+
+import com.example.android.musicservicedemo.BrowserService;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class to get a list of MusicTrack's based on a server-side JSON
+ * configuration.
+ */
+public class MusicProvider {
+
+ private static final String TAG = "MusicProvider";
+
+ private static final String MUSIC_URL = "http://storage.googleapis.com/automotive-media/music.json";
+
+ private static String MUSIC = "music";
+ private static String TITLE = "title";
+ private static String ALBUM = "album";
+ private static String ARTIST = "artist";
+ private static String GENRE = "genre";
+ private static String SOURCE = "source";
+ private static String IMAGE = "image";
+ private static String TRACK_NUMBER = "trackNumber";
+ private static String TOTAL_TRACK_COUNT = "totalTrackCount";
+ private static String DURATION = "duration";
+
+ // Cache for music track data
+ private static List<MusicTrack> mMusicList;
+
+ /**
+ * Get the cached list of music tracks
+ *
+ * @return
+ * @throws JSONException
+ */
+ public List<MusicTrack> getMedia() throws JSONException {
+ if (null != mMusicList && mMusicList.size() > 0) {
+ return mMusicList;
+ }
+ return null;
+ }
+
+ /**
+ * Get the list of music tracks from a server and return the list of
+ * MusicTrack objects.
+ *
+ * @return
+ * @throws JSONException
+ */
+ public List<MusicTrack> retreiveMedia() throws JSONException {
+ if (null != mMusicList) {
+ return mMusicList;
+ }
+ int slashPos = MUSIC_URL.lastIndexOf('/');
+ String path = MUSIC_URL.substring(0, slashPos + 1);
+ JSONObject jsonObj = parseUrl(MUSIC_URL);
+
+ try {
+ JSONArray videos = jsonObj.getJSONArray(MUSIC);
+ if (null != videos) {
+ mMusicList = new ArrayList<MusicTrack>();
+ for (int j = 0; j < videos.length(); j++) {
+ JSONObject music = videos.getJSONObject(j);
+ String title = music.getString(TITLE);
+ String album = music.getString(ALBUM);
+ String artist = music.getString(ARTIST);
+ String genre = music.getString(GENRE);
+ String source = music.getString(SOURCE);
+ // Media is stored relative to JSON file
+ if (!source.startsWith("http")) {
+ source = path + source;
+ }
+ String image = music.getString(IMAGE);
+ if (!image.startsWith("http")) {
+ image = path + image;
+ }
+ int trackNumber = music.getInt(TRACK_NUMBER);
+ int totalTrackCount = music.getInt(TOTAL_TRACK_COUNT);
+ int duration = music.getInt(DURATION) * 1000; // ms
+
+ mMusicList.add(new MusicTrack(title, album, artist, genre, source,
+ image, trackNumber, totalTrackCount, duration));
+ }
+ }
+ } catch (NullPointerException e) {
+ Log.e(TAG, "retreiveMedia", e);
+ }
+ return mMusicList;
+ }
+
+ /**
+ * Download a JSON file from a server, parse the content and return the JSON
+ * object.
+ *
+ * @param urlString
+ * @return
+ */
+ private JSONObject parseUrl(String urlString) {
+ InputStream is = null;
+ try {
+ java.net.URL url = new java.net.URL(urlString);
+ URLConnection urlConnection = url.openConnection();
+ is = new BufferedInputStream(urlConnection.getInputStream());
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ urlConnection.getInputStream(), "iso-8859-1"), 8);
+ StringBuilder sb = new StringBuilder();
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ sb.append(line);
+ }
+ return new JSONObject(sb.toString());
+ } catch (Exception e) {
+ Log.d(TAG, "Failed to parse the json for media list", e);
+ return null;
+ } finally {
+ if (null != is) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ public MatrixCursor getRootContainerCurser() {
+ MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION);
+ Uri.Builder pianoBuilder = new Uri.Builder();
+ pianoBuilder.authority(BrowserService.AUTHORITY);
+ pianoBuilder.appendPath(BrowserService.PIANO_BASE_PATH);
+ matrixCursor.addRow(new Object[] {
+ pianoBuilder.build(),
+ BrowserService.PIANO_BASE_PATH,
+ "subtitle",
+ null,
+ 0
+ });
+
+ Uri.Builder voiceBuilder = new Uri.Builder();
+ voiceBuilder.authority(BrowserService.AUTHORITY);
+ voiceBuilder.appendPath(BrowserService.VOICE_BASE_PATH);
+ matrixCursor.addRow(new Object[] {
+ voiceBuilder.build(),
+ BrowserService.VOICE_BASE_PATH,
+ "subtitle",
+ null,
+ 0
+ });
+ return matrixCursor;
+ }
+
+ public MatrixCursor getRootItemCursor(int type) {
+ if (type == BrowserService.NOW_PLAYING) {
+ MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION);
+
+ try {
+ // Just return all of the tracks for now
+ List<MusicTrack> musicTracks = retreiveMedia();
+ for (MusicTrack musicTrack : musicTracks) {
+ Uri.Builder builder = new Uri.Builder();
+ builder.authority(BrowserService.AUTHORITY);
+ builder.appendPath(BrowserService.NOW_PLAYING_PATH);
+ builder.appendPath(musicTrack.getTitle());
+ matrixCursor.addRow(new Object[] {
+ builder.build(),
+ musicTrack.getTitle(),
+ musicTrack.getArtist(),
+ musicTrack.getImage(),
+ PlaybackState.ACTION_PLAY
+ });
+ Log.d(TAG, "Uri " + builder.build());
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "::getRootItemCursor:", e);
+ }
+
+ Log.d(TAG, "cursor: " + matrixCursor.getCount());
+ return matrixCursor;
+ } else if (type == BrowserService.PIANO) {
+ MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION);
+
+ try {
+ List<MusicTrack> musicTracks = retreiveMedia();
+ for (MusicTrack musicTrack : musicTracks) {
+ Uri.Builder builder = new Uri.Builder();
+ builder.authority(BrowserService.AUTHORITY);
+ builder.appendPath(BrowserService.PIANO_BASE_PATH);
+ builder.appendPath(musicTrack.getTitle());
+ matrixCursor.addRow(new Object[] {
+ builder.build(),
+ musicTrack.getTitle(),
+ musicTrack.getArtist(),
+ musicTrack.getImage(),
+ PlaybackState.ACTION_PLAY
+ });
+ Log.d(TAG, "Uri " + builder.build());
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "::getRootItemCursor:", e);
+ }
+
+ Log.d(TAG, "cursor: " + matrixCursor.getCount());
+ return matrixCursor;
+ } else if (type == BrowserService.VOICE) {
+ MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION);
+
+ try {
+ List<MusicTrack> musicTracks = retreiveMedia();
+ for (MusicTrack musicTrack : musicTracks) {
+ Uri.Builder builder = new Uri.Builder();
+ builder.authority(BrowserService.AUTHORITY);
+ builder.appendPath(BrowserService.VOICE_BASE_PATH);
+ builder.appendPath(musicTrack.getTitle());
+ matrixCursor.addRow(new Object[] {
+ builder.build(),
+ musicTrack.getTitle(),
+ musicTrack.getArtist(),
+ musicTrack.getImage(),
+ PlaybackState.ACTION_PLAY
+ });
+ Log.d(TAG, "Uri " + builder.build());
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "::getRootItemCursor:", e);
+ }
+
+ Log.d(TAG, "cursor: " + matrixCursor.getCount());
+ return matrixCursor;
+
+ }
+ return null;
+ }
+}
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTask.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTask.java
new file mode 100644
index 0000000..ffda110
--- /dev/null
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTask.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.musicservicedemo.browser;
+
+import android.os.AsyncTask;
+import android.util.Log;
+
+import org.json.JSONException;
+
+/**
+ * Asynchronous task to retrieve the music data using MusicProvider.
+ */
+public class MusicProviderTask extends AsyncTask<Void, Void, Void> {
+
+ private static final String TAG = "MusicProviderTask";
+
+ MusicProvider mMusicProvider;
+ MusicProviderTaskListener mMusicProviderTaskListener;
+
+ /**
+ * Initialize the task with the provider to download the music data and the
+ * listener to be informed when the task is done.
+ *
+ * @param musicProvider
+ * @param listener
+ */
+ public MusicProviderTask(MusicProvider musicProvider,
+ MusicProviderTaskListener listener) {
+ mMusicProvider = musicProvider;
+ mMusicProviderTaskListener = listener;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see android.os.AsyncTask#doInBackground(java.lang.Object[])
+ */
+ @Override
+ protected Void doInBackground(Void... arg0) {
+ try {
+ mMusicProvider.retreiveMedia();
+ } catch (JSONException e) {
+ Log.e(TAG, "::doInBackground:", e);
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
+ */
+ @Override
+ protected void onPostExecute(Void result) {
+ mMusicProviderTaskListener.onMusicProviderTaskCompleted();
+ }
+
+}
diff --git a/telecomm/java/android/telecomm/InCallCall.aidl b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTaskListener.java
similarity index 61%
copy from telecomm/java/android/telecomm/InCallCall.aidl
copy to tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTaskListener.java
index be2cdf8..b1d168f 100644
--- a/telecomm/java/android/telecomm/InCallCall.aidl
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTaskListener.java
@@ -1,11 +1,11 @@
/*
- * Copyright 2014, The Android Open Source Project
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
*
* 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
+ * 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,
@@ -14,6 +14,11 @@
* limitations under the License.
*/
-package android.telecomm;
+package com.example.android.musicservicedemo.browser;
-parcelable InCallCall;
+/**
+ * Callback listener for completion of MusicProviderTask
+ */
+public interface MusicProviderTaskListener {
+ public void onMusicProviderTaskCompleted();
+}
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicTrack.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicTrack.java
new file mode 100644
index 0000000..02ea899
--- /dev/null
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicTrack.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.musicservicedemo.browser;
+
+/**
+ * A class to model music track metadata.
+ */
+public class MusicTrack {
+
+ private static final String TAG = "MusicTrack";
+
+ private String mTitle;
+ private String mAlbum;
+ private String mArtist;
+ private String mGenre;
+ private String mSource;
+ private String mImage;
+ private int mTrackNumber;
+ private int mTotalTrackCount;
+ private int mDuration;
+
+ /**
+ * Constructor creating a MusicTrack instance.
+ *
+ * @param title
+ * @param album
+ * @param artist
+ * @param genre
+ * @param source
+ * @param image
+ * @param trackNumber
+ * @param totalTrackCount
+ * @param duration
+ */
+ public MusicTrack(String title, String album, String artist, String genre, String source,
+ String image, int trackNumber, int totalTrackCount, int duration) {
+ this.mTitle = title;
+ this.mAlbum = album;
+ this.mArtist = artist;
+ this.mGenre = genre;
+ this.mSource = source;
+ this.mImage = image;
+ this.mTrackNumber = trackNumber;
+ this.mTotalTrackCount = totalTrackCount;
+ this.mDuration = duration;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(String mTitle) {
+ this.mTitle = mTitle;
+ }
+
+ public String getAlbum() {
+ return mAlbum;
+ }
+
+ public void setAlbum(String mAlbum) {
+ this.mAlbum = mAlbum;
+ }
+
+ public String getArtist() {
+ return mArtist;
+ }
+
+ public void setArtist(String mArtist) {
+ this.mArtist = mArtist;
+ }
+
+ public String getGenre() {
+ return mGenre;
+ }
+
+ public void setGenre(String mGenre) {
+ this.mGenre = mGenre;
+ }
+
+ public String getSource() {
+ return mSource;
+ }
+
+ public void setSource(String mSource) {
+ this.mSource = mSource;
+ }
+
+ public String getImage() {
+ return mImage;
+ }
+
+ public void setImage(String mImage) {
+ this.mImage = mImage;
+ }
+
+ public int getTrackNumber() {
+ return mTrackNumber;
+ }
+
+ public void setTrackNumber(int mTrackNumber) {
+ this.mTrackNumber = mTrackNumber;
+ }
+
+ public int getTotalTrackCount() {
+ return mTotalTrackCount;
+ }
+
+ public void setTotalTrackCount(int mTotalTrackCount) {
+ this.mTotalTrackCount = mTotalTrackCount;
+ }
+
+ public int getDuration() {
+ return mDuration;
+ }
+
+ public void setDuration(int mDuration) {
+ this.mDuration = mDuration;
+ }
+
+ public String toString() {
+ return mTitle;
+ }
+
+}
diff --git a/tests/UsageStatsTest/Android.mk b/tests/UsageStatsTest/Android.mk
new file mode 100644
index 0000000..69fefeb
--- /dev/null
+++ b/tests/UsageStatsTest/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := UsageStatsTest
+
+include $(BUILD_PACKAGE)
diff --git a/tests/UsageStatsTest/AndroidManifest.xml b/tests/UsageStatsTest/AndroidManifest.xml
new file mode 100644
index 0000000..fac5810
--- /dev/null
+++ b/tests/UsageStatsTest/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.usagestats">
+
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
+ <application android:label="Usage Access Test">
+ <activity android:name=".UsageStatsActivity"
+ android:label="Device Usage History">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/UsageStatsTest/res/layout/row_item.xml b/tests/UsageStatsTest/res/layout/row_item.xml
new file mode 100644
index 0000000..da50163
--- /dev/null
+++ b/tests/UsageStatsTest/res/layout/row_item.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp">
+
+ <TextView android:id="@android:id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:textStyle="bold"/>
+
+ <TextView android:id="@android:id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_alignBaseline="@android:id/text1"/>
+</RelativeLayout>
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
new file mode 100644
index 0000000..73143c5
--- /dev/null
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.usagestats;
+
+import android.app.ListActivity;
+import android.app.usage.PackageUsageStats;
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.text.format.DateUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+
+public class UsageStatsActivity extends ListActivity {
+
+ private UsageStatsManager mUsageStatsManager;
+ private Adapter mAdapter;
+ private Comparator<PackageUsageStats> mComparator = new Comparator<PackageUsageStats>() {
+ @Override
+ public int compare(PackageUsageStats o1, PackageUsageStats o2) {
+ return Long.compare(o2.getTotalTimeSpent(), o1.getTotalTimeSpent());
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
+ mAdapter = new Adapter();
+ updateAdapter();
+ setListAdapter(mAdapter);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ updateAdapter();
+ }
+
+ private void updateAdapter() {
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.DAY_OF_YEAR, -14);
+ UsageStats stats = mUsageStatsManager.getRecentStatsSince(cal.getTimeInMillis());
+ mAdapter.update(stats);
+ }
+
+ private class Adapter extends BaseAdapter {
+ private ArrayList<PackageUsageStats> mStats = new ArrayList<>();
+
+ public void update(UsageStats stats) {
+ mStats.clear();
+ if (stats == null) {
+ return;
+ }
+
+ final int packageCount = stats.getPackageCount();
+ for (int i = 0; i < packageCount; i++) {
+ mStats.add(stats.getPackage(i));
+ }
+
+ Collections.sort(mStats, mComparator);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return mStats.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mStats.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final ViewHolder holder;
+ if (convertView == null) {
+ convertView = LayoutInflater.from(UsageStatsActivity.this)
+ .inflate(R.layout.row_item, parent, false);
+ holder = new ViewHolder();
+ holder.packageName = (TextView) convertView.findViewById(android.R.id.text1);
+ holder.usageTime = (TextView) convertView.findViewById(android.R.id.text2);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ holder.packageName.setText(mStats.get(position).getPackageName());
+ holder.usageTime.setText(DateUtils.formatDuration(
+ mStats.get(position).getTotalTimeSpent()));
+ return convertView;
+ }
+ }
+
+ private static class ViewHolder {
+ TextView packageName;
+ TextView usageTime;
+ }
+}
\ No newline at end of file
diff --git a/tests/UsesFeature2Test/Android.mk b/tests/UsesFeature2Test/Android.mk
new file mode 100644
index 0000000..cc784d7
--- /dev/null
+++ b/tests/UsesFeature2Test/Android.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_PACKAGE_NAME := UsesFeature2Test
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/UsesFeature2Test/AndroidManifest.xml b/tests/UsesFeature2Test/AndroidManifest.xml
new file mode 100644
index 0000000..724d186
--- /dev/null
+++ b/tests/UsesFeature2Test/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.usesfeature2">
+
+ <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="19" />
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+
+ <uses-feature android:name="android.hardware.sensor.accelerometer" />
+ <feature-group android:label="@string/minimal">
+ <uses-feature android:name="android.hardware.dpad" />
+ <uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" />
+ </feature-group>
+ <feature-group android:label="@string/gamepad">
+ <uses-feature android:name="android.hardware.gamepad" />
+ </feature-group>
+
+ <application android:label="@string/app_title">
+ <activity android:name="ActivityMain">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/UsesFeature2Test/res/values/values.xml b/tests/UsesFeature2Test/res/values/values.xml
new file mode 100644
index 0000000..2ee9107
--- /dev/null
+++ b/tests/UsesFeature2Test/res/values/values.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="app_title">Uses Feature 2.0</string>
+ <string name="minimal">Crippled experience</string>
+ <string name="gamepad">Gamer experience</string>
+</resources>
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index a16b749..ee62e5e 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -100,7 +100,7 @@
</activity>
<activity
android:name="VectorDrawableStaticPerf"
- android:label="Performance of vector images" >
+ android:label="System icons" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
index 66a9452..3b01e02 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
@@ -26,7 +26,7 @@
<group>
<path
android:name="box1"
- android:pathData="m20,200l100,90l180,-180l-35,-35l-145,145l-60,-60l-40,40z"
+ android:pathData="m20,200l100,90l180-180l-35-35l-145,145l-60-60l-40,40z"
android:fill="?android:attr/colorControlActivated"
android:stroke="?android:attr/colorControlActivated"
android:strokeLineCap="round"
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
index 2fdb676..cd2fd47 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
@@ -35,16 +35,16 @@
M 0, 6.125
l 7.3, 0
l 0, 12.25
- l -7.3, 0
+ l-7.3, 0
z" />
</group>
<group>
<path
android:name="one"
android:fill="#ff88ff"
- android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
- l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
- l -5.046875,0.0 0.0,-1.0Z" />
+ android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0-6.671875-2.109375,0.421875 0.0-1.078125
+ l 2.09375-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
+ l-5.046875,0.0 0.0-1.0Z" />
</group>
<group
android:pivotX="3.65"
@@ -57,22 +57,22 @@
M 0, 0
l 7.3, 0
l 0, 6.125
- l -7.3, 0
+ l-7.3, 0
z" />
</group>
<group>
<path
android:name="two"
android:fill="#ff88ff"
- android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
- q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
- q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
- q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
- q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875
- q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875
- q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
- q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
- q -0.78125024,0.8125 -2.2187502,2.265625Z" />
+ android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0-5.5625,0.0 0.0-1.0q 0.671875-0.6875 1.828125-1.859375
+ q 1.1718752-1.1875 1.4687502-1.53125 0.578125-0.625 0.796875-1.0625
+ q 0.234375-0.453125 0.234375-0.875 0.0-0.703125-0.5-1.140625
+ q-0.484375-0.4375-1.2656252-0.4375-0.5625,0.0-1.1875,0.1875
+ q-0.609375,0.1875-1.3125,0.59375l 0.0-1.203125q 0.71875-0.28125 1.328125-0.421875
+ q 0.625-0.15625 1.140625-0.15625 1.3593752,0.0 2.1718752,0.6875
+ q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125-0.203125,1.015625
+ q-0.203125,0.484375-0.734375,1.140625-0.15625,0.171875-0.9375,0.984375
+ q-0.78125024,0.8125-2.2187502,2.265625Z" />
</group>
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
index 296e0261..d57ae8b 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
@@ -28,41 +28,41 @@
android:name="clip1"
android:pathData="
M 3.65, 6.125
- m -.001, 0
+ m-.001, 0
a .001,.001 0 1,0 .002,0
- a .001,.001 0 1,0 -.002,0z"
+ a .001,.001 0 1,0-.002,0z"
android:clipToPath="true"
android:fill="#112233"
/>
<path
android:name="one"
- android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
- l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
- l -5.046875,0.0 0.0,-1.0Z"
+ android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0-6.671875-2.109375,0.421875 0.0-1.078125
+ l 2.09375-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
+ l-5.046875,0.0 0.0-1.0Z"
android:fill="#ff88ff"
/>
<path
android:name="clip2"
android:pathData="
M 3.65, 6.125
- m -6, 0
+ m-6, 0
a 6,6 0 1,0 12,0
- a 6,6 0 1,0 -12,0z"
+ a 6,6 0 1,0-12,0z"
android:clipToPath="true"
android:fill="#112233"
/>
<path
android:name="two"
- android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
- q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
- q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
- q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
- q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875
- q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875
- q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
- q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
- q -0.78125024,0.8125 -2.2187502,2.265625Z"
+ android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0-5.5625,0.0 0.0-1.0q 0.671875-0.6875 1.828125-1.859375
+ q 1.1718752-1.1875 1.4687502-1.53125 0.578125-0.625 0.796875-1.0625
+ q 0.234375-0.453125 0.234375-0.875 0.0-0.703125-0.5-1.140625
+ q-0.484375-0.4375-1.2656252-0.4375-0.5625,0.0-1.1875,0.1875
+ q-0.609375,0.1875-1.3125,0.59375l 0.0-1.203125q 0.71875-0.28125 1.328125-0.421875
+ q 0.625-0.15625 1.140625-0.15625 1.3593752,0.0 2.1718752,0.6875
+ q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125-0.203125,1.015625
+ q-0.203125,0.484375-0.734375,1.140625-0.15625,0.171875-0.9375,0.984375
+ q-0.78125024,0.8125-2.2187502,2.265625Z"
android:fill="#ff88ff"
/>
</group>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
index 1633326..673c465 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
@@ -28,21 +28,21 @@
<path
android:name="one"
android:fill="#ffff00"
- android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
- l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
- l -5.046875,0.0 0.0,-1.0Z" />
+ android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0-6.671875-2.109375,0.421875 0.0-1.078125
+ l 2.09375-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
+ l-5.046875,0.0 0.0-1.0Z" />
<path
android:name="two"
android:fill="#ffff00"
android:fillOpacity="0"
- android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
- q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
- q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
- q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
- q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875
- q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875
- q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
- q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
- q -0.78125024,0.8125 -2.2187502,2.265625Z" />
+ android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0-5.5625,0.0 0.0-1.0q 0.671875-0.6875 1.828125-1.859375
+ q 1.1718752-1.1875 1.4687502-1.53125 0.578125-0.625 0.796875-1.0625
+ q 0.234375-0.453125 0.234375-0.875 0.0-0.703125-0.5-1.140625
+ q-0.484375-0.4375-1.2656252-0.4375-0.5625,0.0-1.1875,0.1875
+ q-0.609375,0.1875-1.3125,0.59375l 0.0-1.203125q 0.71875-0.28125 1.328125-0.421875
+ q 0.625-0.15625 1.140625-0.15625 1.3593752,0.0 2.1718752,0.6875
+ q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125-0.203125,1.015625
+ q-0.203125,0.484375-0.734375,1.140625-0.15625,0.171875-0.9375,0.984375
+ q-0.78125024,0.8125-2.2187502,2.265625Z" />
</group>
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml
index 7c7e679..ccb0df0 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml
@@ -23,9 +23,9 @@
<group>
<path
android:name="back"
- android:pathData="M 20,55 l 35.3,-35.3 7.07,7.07 -35.3,35.3 z
- M 27,50 l 97,0 0,10 -97,0 z
- M 20,55 l 7.07,-7.07 35.3,35.3 -7.07,7.07 z"
+ android:pathData="M 20,55 l 35.3-35.3 7.07,7.07-35.3,35.3 z
+ M 27,50 l 97,0 0,10-97,0 z
+ M 20,55 l 7.07-7.07 35.3,35.3-7.07,7.07 z"
android:fill="#ffffffff"
/>
</group>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml
index c93c85f..77434fc 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml
@@ -30,7 +30,7 @@
<path
android:name="house"
android:fill="#ffffffff"
- android:pathData="M 100,20 l 0,0 0,140 -80,0 z M 100,20 l 0,0 80,140 -80,0 z"/>
+ android:pathData="M 100,20 l 0,0 0,140-80,0 z M 100,20 l 0,0 80,140-80,0 z"/>
</group>
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml
index 8484e9e..df24713 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml
@@ -28,19 +28,19 @@
<path
android:name="bar3"
android:fill="#FFFFFFFF"
- android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" />
+ android:pathData="M49.001,60c-5.466,0-9.899,4.478-9.899,10s4.434,10,9.899,10c5.468,0,9.899-4.478,9.899-10S54.469,60,49.001,60z" />
<path
android:name="bar2"
android:fill="#FFFFFFFF"
- android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" />
+ android:pathData="M28.001,48.787l7,7.07c7.731-7.811,20.269-7.81,28.001,0l6.999-7.07C58.403,37.071,39.599,37.071,28.001,48.787z" />
<path
android:name="bar1"
android:fill="#FF555555"
- android:pathData="M14.001,34.645 L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" />
+ android:pathData="M14.001,34.645 L21,41.716c15.464-15.621,40.536-15.621,56,0l7.001-7.071C64.672,15.119,33.33,15.119,14.001,34.645z" />
<path
android:name="bar0"
android:fill="#FF555555"
- android:pathData="M0,20.502l6.999,7.071 c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" />
+ android:pathData="M0,20.502l6.999,7.071 c23.196-23.431,60.806-23.431,84.002,0L98,20.503C70.938-6.834,27.063-6.834,0,20.502z" />
</group>
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_favorite.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable_favorite.xml
index c8840f5..5a66e2d 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable_favorite.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable_favorite.xml
@@ -32,11 +32,11 @@
android:fill="#ff000000"
android:pathData="M2.100006104,-6
C0.1449127197,-6,1.600006104,-5.975006104,0,-5.975006104
- C-1.574996948,-5.975006104,0.00309753418,-6,-1.949996948,-6
+ C-1.574996948,-5.975006104,0.00309753418,-6-1.949996948-6
C-4.492996216,-6,-5.949996948,-3.718399048,-5.949996948,-1.149993896
C-5.949996948,2.379302979,-5.699996948,5.100006104,0,5.100006104
C5.699996948,5.100006104,6,2.379302979,6,-1.149993896
- C6,-3.718399048,4.643005371,-6,2.100006104,-6" />
+ C6,-3.718399048,4.643005371-6,2.100006104-6" />
</group>
</vector>
\ No newline at end of file
diff --git a/tests/VoiceInteraction/AndroidManifest.xml b/tests/VoiceInteraction/AndroidManifest.xml
index 33f000d..c328b3c 100644
--- a/tests/VoiceInteraction/AndroidManifest.xml
+++ b/tests/VoiceInteraction/AndroidManifest.xml
@@ -1,6 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test.voiceinteraction">
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
<application>
<activity android:name="VoiceInteractionMain" android:label="Voice Interaction"
android:theme="@android:style/Theme.Material">
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
index e74307f..cc710f9 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -30,6 +30,12 @@
private final Callback mHotwordCallback = new Callback() {
@Override
+ public void onAvailabilityChanged(int status) {
+ Log.i(TAG, "onAvailabilityChanged(" + status + ")");
+ hotwordAvailabilityChangeHelper(status);
+ }
+
+ @Override
public void onDetected(byte[] data) {
Log.i(TAG, "onDetected");
}
@@ -50,30 +56,7 @@
Log.i(TAG, "Keyphrase enrollment meta-data: "
+ Arrays.toString(getKeyphraseEnrollmentInfo().listKeyphraseMetadata()));
- mHotwordDetector = getAlwaysOnHotwordDetector("Hello There", "en-US", mHotwordCallback);
- int availability = mHotwordDetector.getAvailability();
- Log.i(TAG, "Hotword availability = " + availability);
-
- switch (availability) {
- case AlwaysOnHotwordDetector.KEYPHRASE_HARDWARE_UNAVAILABLE:
- Log.i(TAG, "KEYPHRASE_HARDWARE_UNAVAILABLE");
- break;
- case AlwaysOnHotwordDetector.KEYPHRASE_UNSUPPORTED:
- Log.i(TAG, "KEYPHRASE_UNSUPPORTED");
- break;
- case AlwaysOnHotwordDetector.KEYPHRASE_UNENROLLED:
- Log.i(TAG, "KEYPHRASE_UNENROLLED");
- Intent enroll = mHotwordDetector.getManageIntent(
- AlwaysOnHotwordDetector.MANAGE_ACTION_ENROLL);
- Log.i(TAG, "Need to enroll with " + enroll);
- break;
- case AlwaysOnHotwordDetector.KEYPHRASE_ENROLLED:
- Log.i(TAG, "KEYPHRASE_ENROLLED");
- int status = mHotwordDetector.startRecognition(
- AlwaysOnHotwordDetector.RECOGNITION_FLAG_NONE);
- Log.i(TAG, "startRecognition status = " + status);
- break;
- }
+ mHotwordDetector = createAlwaysOnHotwordDetector("Hello There", "en-US", mHotwordCallback);
}
@Override
@@ -84,4 +67,33 @@
stopSelf(startId);
return START_NOT_STICKY;
}
+
+ private void hotwordAvailabilityChangeHelper(int availability) {
+ Log.i(TAG, "Hotword availability = " + availability);
+ switch (availability) {
+ case AlwaysOnHotwordDetector.STATE_INVALID:
+ Log.i(TAG, "STATE_INVALID");
+ mHotwordDetector =
+ createAlwaysOnHotwordDetector("Hello There", "en-US", mHotwordCallback);
+ break;
+ case AlwaysOnHotwordDetector.STATE_HARDWARE_UNAVAILABLE:
+ Log.i(TAG, "STATE_HARDWARE_UNAVAILABLE");
+ break;
+ case AlwaysOnHotwordDetector.STATE_KEYPHRASE_UNSUPPORTED:
+ Log.i(TAG, "STATE_KEYPHRASE_UNSUPPORTED");
+ break;
+ case AlwaysOnHotwordDetector.STATE_KEYPHRASE_UNENROLLED:
+ Log.i(TAG, "STATE_KEYPHRASE_UNENROLLED");
+ Intent enroll = mHotwordDetector.getManageIntent(
+ AlwaysOnHotwordDetector.MANAGE_ACTION_ENROLL);
+ Log.i(TAG, "Need to enroll with " + enroll);
+ break;
+ case AlwaysOnHotwordDetector.STATE_KEYPHRASE_ENROLLED:
+ Log.i(TAG, "STATE_KEYPHRASE_ENROLLED");
+ int status = mHotwordDetector.startRecognition(
+ AlwaysOnHotwordDetector.RECOGNITION_FLAG_NONE);
+ Log.i(TAG, "startRecognition status = " + status);
+ break;
+ }
+ }
}
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 5fefab6..ac1ae70 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -4,20 +4,23 @@
// Android Asset Packaging Tool main entry point.
//
#include "ApkBuilder.h"
-#include "Main.h"
#include "Bundle.h"
+#include "Images.h"
+#include "Main.h"
#include "ResourceFilter.h"
#include "ResourceTable.h"
-#include "Images.h"
#include "XMLNode.h"
-#include <utils/Log.h>
-#include <utils/threads.h>
-#include <utils/List.h>
#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/Log.h>
+#include <utils/SortedVector.h>
+#include <utils/threads.h>
+#include <utils/Vector.h>
-#include <fcntl.h>
#include <errno.h>
+#include <fcntl.h>
using namespace android;
@@ -588,6 +591,106 @@
printf("provides-component:'%s'\n", componentName);
}
+/**
+ * Represents a feature that has been automatically added due to
+ * a pre-requisite or some other reason.
+ */
+struct ImpliedFeature {
+ /**
+ * Name of the implied feature.
+ */
+ String8 name;
+
+ /**
+ * List of human-readable reasons for why this feature was implied.
+ */
+ SortedVector<String8> reasons;
+};
+
+/**
+ * Represents a <feature-group> tag in the AndroidManifest.xml
+ */
+struct FeatureGroup {
+ /**
+ * Human readable label
+ */
+ String8 label;
+
+ /**
+ * Explicit features defined in the group
+ */
+ KeyedVector<String8, bool> features;
+};
+
+static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
+ const char* name, const char* reason) {
+ String8 name8(name);
+ ssize_t idx = impliedFeatures->indexOfKey(name8);
+ if (idx < 0) {
+ idx = impliedFeatures->add(name8, ImpliedFeature());
+ impliedFeatures->editValueAt(idx).name = name8;
+ }
+ impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
+}
+
+static void printFeatureGroup(const FeatureGroup& grp,
+ const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
+ printf("feature-group: label='%s'\n", grp.label.string());
+
+ const size_t numFeatures = grp.features.size();
+ for (size_t i = 0; i < numFeatures; i++) {
+ if (!grp.features[i]) {
+ continue;
+ }
+
+ const String8& featureName = grp.features.keyAt(i);
+ printf(" uses-feature: name='%s'\n",
+ ResTable::normalizeForOutput(featureName.string()).string());
+ }
+
+ const size_t numImpliedFeatures =
+ (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
+ for (size_t i = 0; i < numImpliedFeatures; i++) {
+ const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
+ if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
+ // The feature is explicitly set, no need to use implied
+ // definition.
+ continue;
+ }
+
+ String8 printableFeatureName(ResTable::normalizeForOutput(
+ impliedFeature.name.string()));
+ printf(" uses-feature: name='%s'\n", printableFeatureName.string());
+ printf(" uses-implied-feature: name='%s' reason='",
+ printableFeatureName.string());
+ const size_t numReasons = impliedFeature.reasons.size();
+ for (size_t j = 0; j < numReasons; j++) {
+ printf("%s", impliedFeature.reasons[j].string());
+ if (j + 2 < numReasons) {
+ printf(", ");
+ } else if (j + 1 < numReasons) {
+ printf(", and ");
+ }
+ }
+ printf("'\n");
+ }
+}
+
+static void addParentFeatures(FeatureGroup* grp, const String8& name) {
+ if (name == "android.hardware.camera.autofocus" ||
+ name == "android.hardware.camera.flash") {
+ grp->features.add(String8("android.hardware.camera"), true);
+ } else if (name == "android.hardware.location.gps" ||
+ name == "android.hardware.location.network") {
+ grp->features.add(String8("android.hardware.location"), true);
+ } else if (name == "android.hardware.touchscreen.multitouch") {
+ grp->features.add(String8("android.hardware.touchscreen"), true);
+ } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
+ grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
+ grp->features.add(String8("android.hardware.touchscreen"), true);
+ }
+}
+
/*
* Handle the "dump" command, to extract select data from an archive.
*/
@@ -797,6 +900,7 @@
bool isSearchable = false;
bool withinApplication = false;
bool withinSupportsInput = false;
+ bool withinFeatureGroup = false;
bool withinReceiver = false;
bool withinService = false;
bool withinProvider = false;
@@ -869,36 +973,7 @@
// some new uses-feature constants in 2.1 and 2.2. In most cases, the
// heuristic is "if an app requests a permission but doesn't explicitly
// request the corresponding <uses-feature>, presume it's there anyway".
- bool specCameraFeature = false; // camera-related
- bool specCameraAutofocusFeature = false;
- bool reqCameraAutofocusFeature = false;
- bool reqCameraFlashFeature = false;
- bool hasCameraPermission = false;
- bool specLocationFeature = false; // location-related
- bool specNetworkLocFeature = false;
- bool reqNetworkLocFeature = false;
- bool specGpsFeature = false;
- bool reqGpsFeature = false;
- bool hasMockLocPermission = false;
- bool hasCoarseLocPermission = false;
- bool hasGpsPermission = false;
- bool hasGeneralLocPermission = false;
- bool specBluetoothFeature = false; // Bluetooth API-related
- bool hasBluetoothPermission = false;
- bool specMicrophoneFeature = false; // microphone-related
- bool hasRecordAudioPermission = false;
- bool specWiFiFeature = false;
- bool hasWiFiPermission = false;
- bool specTelephonyFeature = false; // telephony-related
- bool reqTelephonySubFeature = false;
- bool hasTelephonyPermission = false;
- bool specTouchscreenFeature = false; // touchscreen-related
- bool specMultitouchFeature = false;
- bool reqDistinctMultitouchFeature = false;
- bool specScreenPortraitFeature = false;
- bool specScreenLandscapeFeature = false;
- bool reqScreenPortraitFeature = false;
- bool reqScreenLandscapeFeature = false;
+
// 2.2 also added some other features that apps can request, but that
// have no corresponding permission, so we cannot implement any
// back-compatibility heuristic for them. The below are thus unnecessary
@@ -926,6 +1001,11 @@
String8 receiverName;
String8 serviceName;
Vector<String8> supportedInput;
+
+ FeatureGroup commonFeatures;
+ Vector<FeatureGroup> featureGroups;
+ KeyedVector<String8, ImpliedFeature> impliedFeatures;
+
while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
depth--;
@@ -946,6 +1026,7 @@
}
withinApplication = false;
withinSupportsInput = false;
+ withinFeatureGroup = false;
} else if (depth < 3) {
if (withinActivity && isMainActivity) {
String8 aName(getComponentName(pkg, activityName));
@@ -1210,59 +1291,27 @@
COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
largestWidthLimitDp = getIntegerAttribute(tree,
LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
+ } else if (tag == "feature-group") {
+ withinFeatureGroup = true;
+ FeatureGroup group;
+ group.label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'android:label' attribute:"
+ " %s\n", error.string());
+ goto bail;
+ }
+ featureGroups.add(group);
+
} else if (tag == "uses-feature") {
String8 name = getAttribute(tree, NAME_ATTR, &error);
-
if (name != "" && error == "") {
int req = getIntegerAttribute(tree,
REQUIRED_ATTR, NULL, 1);
- if (name == "android.hardware.camera") {
- specCameraFeature = true;
- } else if (name == "android.hardware.camera.autofocus") {
- // these have no corresponding permission to check for,
- // but should imply the foundational camera permission
- reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
- specCameraAutofocusFeature = true;
- } else if (req && (name == "android.hardware.camera.flash")) {
- // these have no corresponding permission to check for,
- // but should imply the foundational camera permission
- reqCameraFlashFeature = true;
- } else if (name == "android.hardware.location") {
- specLocationFeature = true;
- } else if (name == "android.hardware.location.network") {
- specNetworkLocFeature = true;
- reqNetworkLocFeature = reqNetworkLocFeature || req;
- } else if (name == "android.hardware.location.gps") {
- specGpsFeature = true;
- reqGpsFeature = reqGpsFeature || req;
- } else if (name == "android.hardware.bluetooth") {
- specBluetoothFeature = true;
- } else if (name == "android.hardware.touchscreen") {
- specTouchscreenFeature = true;
- } else if (name == "android.hardware.touchscreen.multitouch") {
- specMultitouchFeature = true;
- } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
- reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
- } else if (name == "android.hardware.microphone") {
- specMicrophoneFeature = true;
- } else if (name == "android.hardware.wifi") {
- specWiFiFeature = true;
- } else if (name == "android.hardware.telephony") {
- specTelephonyFeature = true;
- } else if (req && (name == "android.hardware.telephony.gsm" ||
- name == "android.hardware.telephony.cdma")) {
- // these have no corresponding permission to check for,
- // but should imply the foundational telephony permission
- reqTelephonySubFeature = true;
- } else if (name == "android.hardware.screen.portrait") {
- specScreenPortraitFeature = true;
- } else if (name == "android.hardware.screen.landscape") {
- specScreenLandscapeFeature = true;
+ commonFeatures.features.add(name, req);
+ if (req) {
+ addParentFeatures(&commonFeatures, name);
}
- printf("uses-feature%s:'%s'\n",
- req ? "" : "-not-required",
- ResTable::normalizeForOutput(name.string()).string());
} else {
int vers = getIntegerAttribute(tree,
GL_ES_VERSION_ATTR, &error);
@@ -1274,25 +1323,51 @@
String8 name = getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
if (name == "android.permission.CAMERA") {
- hasCameraPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.feature",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.ACCESS_FINE_LOCATION") {
- hasGpsPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
+ String8::format("requested %s permission", name.string())
+ .string());
+ addImpliedFeature(&impliedFeatures, "android.hardware.location",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
- hasMockLocPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.location",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
- hasCoarseLocPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
+ String8::format("requested %s permission", name.string())
+ .string());
+ addImpliedFeature(&impliedFeatures, "android.hardware.location",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
name == "android.permission.INSTALL_LOCATION_PROVIDER") {
- hasGeneralLocPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.location",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.BLUETOOTH" ||
name == "android.permission.BLUETOOTH_ADMIN") {
- hasBluetoothPermission = true;
+ if (targetSdk > 4) {
+ addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
+ String8::format("requested %s permission", name.string())
+ .string());
+ addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
+ "targetSdkVersion > 4");
+ }
} else if (name == "android.permission.RECORD_AUDIO") {
- hasRecordAudioPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.ACCESS_WIFI_STATE" ||
name == "android.permission.CHANGE_WIFI_STATE" ||
name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
- hasWiFiPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.CALL_PHONE" ||
name == "android.permission.CALL_PRIVILEGED" ||
name == "android.permission.MODIFY_PHONE_STATE" ||
@@ -1304,7 +1379,8 @@
name == "android.permission.SEND_SMS" ||
name == "android.permission.WRITE_APN_SETTINGS" ||
name == "android.permission.WRITE_SMS") {
- hasTelephonyPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
+ String8("requested a telephony permission").string());
} else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
hasWriteExternalStoragePermission = true;
} else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
@@ -1430,10 +1506,12 @@
if (error == "") {
if (orien == 0 || orien == 6 || orien == 8) {
// Requests landscape, sensorLandscape, or reverseLandscape.
- reqScreenLandscapeFeature = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
+ "one or more activities have specified a landscape orientation");
} else if (orien == 1 || orien == 7 || orien == 9) {
// Requests portrait, sensorPortrait, or reversePortrait.
- reqScreenPortraitFeature = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
+ "one or more activities have specified a portrait orientation");
}
}
} else if (tag == "uses-library") {
@@ -1560,6 +1638,20 @@
goto bail;
}
}
+ } else if (withinFeatureGroup && tag == "uses-feature") {
+ String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
+ error.string());
+ goto bail;
+ }
+
+ int required = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
+ FeatureGroup& top = featureGroups.editTop();
+ top.features.add(name, required);
+ if (required) {
+ addParentFeatures(&top, name);
+ }
}
} else if (depth == 4) {
if (tag == "intent-filter") {
@@ -1734,137 +1826,34 @@
}
}
- /* The following blocks handle printing "inferred" uses-features, based
- * on whether related features or permissions are used by the app.
- * Note that the various spec*Feature variables denote whether the
- * relevant tag was *present* in the AndroidManfest, not that it was
- * present and set to true.
- */
- // Camera-related back-compatibility logic
- if (!specCameraFeature) {
- 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
- // (which was the 1.0 - 1.5 behavior)
- 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");
+ addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
+ "default feature for all apps");
+
+ const size_t numFeatureGroups = featureGroups.size();
+ if (numFeatureGroups == 0) {
+ // If no <feature-group> tags were defined, apply auto-implied features.
+ printFeatureGroup(commonFeatures, &impliedFeatures);
+
+ } else {
+ // <feature-group> tags are defined, so we ignore implied features and
+ for (size_t i = 0; i < numFeatureGroups; i++) {
+ FeatureGroup& grp = featureGroups.editItemAt(i);
+
+ // Merge the features defined in the top level (not inside a <feature-group>)
+ // with this feature group.
+ const size_t numCommonFeatures = commonFeatures.features.size();
+ for (size_t j = 0; j < numCommonFeatures; j++) {
+ if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
+ grp.features.add(commonFeatures.features.keyAt(j), commonFeatures.features[j]);
+ }
+ }
+
+ if (!grp.features.isEmpty()) {
+ printFeatureGroup(grp);
}
}
}
- // Location-related back-compatibility logic
- if (!specLocationFeature &&
- (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
- hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
- // 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_COARSE_LOCATION permission'\n");
- }
-
- // Bluetooth-related compatibility logic
- if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
- // 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
- if (!specMicrophoneFeature && hasRecordAudioPermission) {
- // 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
- if (!specWiFiFeature && hasWiFiPermission) {
- // 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
- if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
- // 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
- if (!specTouchscreenFeature) { // not a typo!
- // all apps are presumed to require a touchscreen, unless they explicitly say
- // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
- // 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
- if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
- // If the app has specified any activities in its manifest
- // that request a specific orientation, then assume that
- // 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");
- }
- }
if (hasWidgetReceivers) {
printComponentPresence("app-widget");
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
index ead3b13..9300401 100644
--- a/tools/layoutlib/Android.mk
+++ b/tools/layoutlib/Android.mk
@@ -27,8 +27,8 @@
# We need to process the framework classes.jar file, but we can't
# depend directly on it (private vars won't be inherited correctly).
# So, we depend on framework's BUILT file.
-built_framework_dep := $(call java-lib-deps,framework-base)
-built_framework_classes := $(call java-lib-files,framework-base)
+built_framework_dep := $(call java-lib-deps,framework)
+built_framework_classes := $(call java-lib-files,framework)
built_core_dep := $(call java-lib-deps,core-libart)
built_core_classes := $(call java-lib-files,core-libart)
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index 7153a90..776398f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -608,6 +608,9 @@
* @param y The y-coordinate of the end of a line
*/
private void lineTo(float x, float y) {
+ if (isEmpty()) {
+ mPath.moveTo(mLastX = 0, mLastY = 0);
+ }
mPath.lineTo(mLastX = x, mLastY = y);
}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 6927b26..2ffe4a2 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -360,6 +360,11 @@
}
@Override
+ public void updateScreenCaptureDisabled(int userId) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
public void updateRotation(boolean arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
}
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 0dbf5ca..4a4b9ff 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -70,7 +70,7 @@
/** @hide */
public static final int AUTO_JOIN_DISABLED = 32;
/** @hide */
- public static final int AUTHENTICATIO_ERROR = 128;
+ public static final int AUTHENTICATION_ERROR = 128;
/**
* Status: indicating join status
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index a6c2cc3..94e9a5d 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -25,6 +25,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.annotation.SystemApi;
import java.util.HashMap;
import java.util.BitSet;
@@ -356,6 +357,7 @@
* @hide
* Uid of last app modifying the configuration
*/
+ @SystemApi
public int lastUpdateUid;
/**
@@ -430,8 +432,12 @@
public static int INITIAL_AUTO_JOIN_ATTEMPT_MIN_5 = -70;
/** @hide
- * 5GHz band is prefered over 2.4 if the 5GHz RSSI is higher than this threshold **/
- public static int A_BAND_PREFERENCE_RSSI_THRESHOLD = -65;
+ * 5GHz band is prefered low over 2.4 if the 5GHz RSSI is higher than this threshold */
+ public static int A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW = -65;
+
+ /** @hide
+ * 5GHz band is prefered hard over 2.4 if the 5GHz RSSI is higher than this threshold */
+ public static int A_BAND_PREFERENCE_RSSI_THRESHOLD = -55;
/** @hide
* 5GHz band is penalized if the 5GHz RSSI is lower than this threshold **/
@@ -582,21 +588,33 @@
*/
public int autoJoinStatus;
+ /**
+ * @hide
+ * Number of connection failures
+ */
+ public int numConnectionFailures;
/**
* @hide
+ * Last time we blacklisted the configuration
*/
public long blackListTimestamp;
/**
* @hide
- * last time the system was connected to this configuration.
+ * Last time the system was connected to this configuration.
*/
public long lastConnected;
/**
* @hide
- * last time the system was disconnected to this configuration.
+ * Last time the system tried to connect and failed.
+ */
+ public long lastConnectionFailure;
+
+ /**
+ * @hide
+ * Last time the system was disconnected to this configuration.
*/
public long lastDisconnected;
@@ -619,7 +637,7 @@
public boolean didSelfAdd;
/**
- * peer WifiConfiguration this WifiConfiguration was added for
+ * Peer WifiConfiguration this WifiConfiguration was added for
* @hide
*/
public String peerWifiConfiguration;
@@ -633,6 +651,24 @@
/**
* @hide
+ * Number of time the scorer overrode a the priority based choice, when comparing two
+ * WifiConfigurations, note that since comparing WifiConfiguration happens very often
+ * potentially at every scan, this number might become very large, even on an idle
+ * system.
+ */
+ @SystemApi
+ public int numScorerOverride;
+
+ /**
+ * @hide
+ * Number of time the scorer overrode a the priority based choice, and the comparison
+ * triggered a network switch
+ */
+ @SystemApi
+ public int numScorerOverrideAndSwitchedNetwork;
+
+ /**
+ * @hide
* Connect choices
*
* remember the keys identifying the known WifiConfiguration over which this configuration
@@ -1148,6 +1184,10 @@
blackListTimestamp = source.blackListTimestamp;
lastConnected = source.lastConnected;
lastDisconnected = source.lastDisconnected;
+ lastConnectionFailure = source.lastConnectionFailure;
+ numConnectionFailures = source.numConnectionFailures;
+ numScorerOverride = source.numScorerOverride;
+ numScorerOverrideAndSwitchedNetwork = source.numScorerOverrideAndSwitchedNetwork;
}
}
@@ -1195,6 +1235,10 @@
dest.writeInt(lastUpdateUid);
dest.writeInt(bssidOwnerUid);
dest.writeLong(blackListTimestamp);
+ dest.writeLong(lastConnectionFailure);
+ dest.writeInt(numConnectionFailures);
+ dest.writeInt(numScorerOverride);
+ dest.writeInt(numScorerOverrideAndSwitchedNetwork);
}
/** Implement the Parcelable interface {@hide} */
@@ -1238,6 +1282,10 @@
config.lastUpdateUid = in.readInt();
config.bssidOwnerUid = in.readInt();
config.blackListTimestamp = in.readLong();
+ config.lastConnectionFailure = in.readLong();
+ config.numConnectionFailures = in.readInt();
+ config.numScorerOverride = in.readInt();
+ config.numScorerOverrideAndSwitchedNetwork = in.readInt();
return config;
}