Merge "Add multiple dir support to build_font.py"
diff --git a/Android.mk b/Android.mk
index de93ca2..8603d99 100644
--- a/Android.mk
+++ b/Android.mk
@@ -78,6 +78,7 @@
core/java/android/app/IServiceConnection.aidl \
core/java/android/app/IStopUserCallback.aidl \
core/java/android/app/task/ITaskCallback.aidl \
+ core/java/android/app/task/ITaskManager.aidl \
core/java/android/app/task/ITaskService.aidl \
core/java/android/app/IThumbnailRetriever.aidl \
core/java/android/app/ITransientNotification.aidl \
@@ -216,6 +217,8 @@
core/java/android/service/wallpaper/IWallpaperEngine.aidl \
core/java/android/service/wallpaper/IWallpaperService.aidl \
core/java/android/tv/ITvInputClient.aidl \
+ core/java/android/tv/ITvInputHardware.aidl \
+ core/java/android/tv/ITvInputHardwareCallback.aidl \
core/java/android/tv/ITvInputManager.aidl \
core/java/android/tv/ITvInputService.aidl \
core/java/android/tv/ITvInputServiceCallback.aidl \
@@ -276,6 +279,7 @@
core/java/com/android/internal/view/IInputMethodSession.aidl \
core/java/com/android/internal/view/IInputSessionCallback.aidl \
core/java/com/android/internal/widget/ILockSettings.aidl \
+ core/java/com/android/internal/widget/ILockSettingsObserver.aidl \
core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
keystore/java/android/security/IKeyChainAliasCallback.aidl \
@@ -325,6 +329,7 @@
telecomm/java/com/android/internal/telecomm/ICallServiceSelectorAdapter.aidl \
telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl \
telecomm/java/com/android/internal/telecomm/IInCallService.aidl \
+ telecomm/java/com/android/internal/telecomm/ITelecommService.aidl \
telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
telephony/java/com/android/internal/telephony/ITelephony.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index c841338..48a20a4 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -191,6 +191,7 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/tv/)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/media/java/android/media/)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/wearable)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/current.txt b/api/current.txt
index c38a82f..e44ea24 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -749,6 +749,7 @@
field public static final int layout_centerVertical = 16843153; // 0x1010191
field public static final int layout_column = 16843084; // 0x101014c
field public static final int layout_columnSpan = 16843645; // 0x101037d
+ field public static final int layout_columnWeight = 16843868; // 0x101045c
field public static final int layout_gravity = 16842931; // 0x10100b3
field public static final int layout_height = 16842997; // 0x10100f5
field public static final int layout_margin = 16842998; // 0x10100f6
@@ -760,6 +761,7 @@
field public static final int layout_marginTop = 16843000; // 0x10100f8
field public static final int layout_row = 16843643; // 0x101037b
field public static final int layout_rowSpan = 16843644; // 0x101037c
+ field public static final int layout_rowWeight = 16843867; // 0x101045b
field public static final int layout_scale = 16843155; // 0x1010193
field public static final int layout_span = 16843085; // 0x101014d
field public static final int layout_toEndOf = 16843704; // 0x10103b8
@@ -1399,8 +1401,6 @@
field public static final int l_resource_pad9 = 17104904; // 0x1050008
field public static final int notification_large_icon_height = 17104902; // 0x1050006
field public static final int notification_large_icon_width = 17104901; // 0x1050005
- field public static final int recents_thumbnail_height = 17104913; // 0x1050011
- field public static final int recents_thumbnail_width = 17104914; // 0x1050012
field public static final int thumbnail_height = 17104897; // 0x1050001
field public static final int thumbnail_width = 17104898; // 0x1050002
}
@@ -4538,13 +4538,22 @@
ctor public Notification.Action.Builder(android.app.Notification.Action);
method public android.app.Notification.Action.Builder addExtras(android.os.Bundle);
method public android.app.Notification.Action.Builder addRemoteInput(android.app.RemoteInput);
- method public android.app.Notification.Action.Builder apply(android.app.Notification.Action.Builder.Extender);
method public android.app.Notification.Action build();
+ method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Extender);
method public android.os.Bundle getExtras();
}
- public static abstract interface Notification.Action.Builder.Extender {
- method public abstract android.app.Notification.Action.Builder applyTo(android.app.Notification.Action.Builder);
+ public static abstract interface Notification.Action.Extender {
+ method public abstract android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
+ }
+
+ public static final class Notification.Action.WearableExtender implements android.app.Notification.Action.Extender {
+ ctor public Notification.Action.WearableExtender();
+ ctor public Notification.Action.WearableExtender(android.app.Notification.Action);
+ method public android.app.Notification.Action.WearableExtender clone();
+ method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
+ method public boolean isAvailableOffline();
+ method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
}
public static class Notification.BigPictureStyle extends android.app.Notification.Style {
@@ -4570,8 +4579,8 @@
method public android.app.Notification.Builder addAction(android.app.Notification.Action);
method public android.app.Notification.Builder addExtras(android.os.Bundle);
method public android.app.Notification.Builder addPerson(java.lang.String);
- method public android.app.Notification.Builder apply(android.app.Notification.Builder.Extender);
method public android.app.Notification build();
+ method public android.app.Notification.Builder extend(android.app.Notification.Extender);
method public android.os.Bundle getExtras();
method public deprecated android.app.Notification getNotification();
method public android.app.Notification.Builder setAutoCancel(boolean);
@@ -4613,8 +4622,8 @@
method public android.app.Notification.Builder setWhen(long);
}
- public static abstract interface Notification.Builder.Extender {
- method public abstract android.app.Notification.Builder applyTo(android.app.Notification.Builder);
+ public static abstract interface Notification.Extender {
+ method public abstract android.app.Notification.Builder extend(android.app.Notification.Builder);
}
public static class Notification.InboxStyle extends android.app.Notification.Style {
@@ -4644,6 +4653,51 @@
field protected android.app.Notification.Builder mBuilder;
}
+ public static final class Notification.WearableExtender implements android.app.Notification.Extender {
+ ctor public Notification.WearableExtender();
+ ctor public Notification.WearableExtender(android.app.Notification);
+ method public android.app.Notification.WearableExtender addAction(android.app.Notification.Action);
+ method public android.app.Notification.WearableExtender addActions(java.util.List<android.app.Notification.Action>);
+ method public android.app.Notification.WearableExtender addPage(android.app.Notification);
+ method public android.app.Notification.WearableExtender addPages(java.util.List<android.app.Notification>);
+ method public android.app.Notification.WearableExtender clearActions();
+ method public android.app.Notification.WearableExtender clearPages();
+ method public android.app.Notification.WearableExtender clone();
+ method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+ method public java.util.List<android.app.Notification.Action> getActions();
+ method public android.graphics.Bitmap getBackground();
+ method public int getContentAction();
+ method public int getContentIcon();
+ method public int getContentIconGravity();
+ method public boolean getContentIntentAvailableOffline();
+ method public int getCustomContentHeight();
+ method public int getCustomSizePreset();
+ method public android.app.PendingIntent getDisplayIntent();
+ method public int getGravity();
+ method public boolean getHintHideIcon();
+ method public boolean getHintShowBackgroundOnly();
+ method public java.util.List<android.app.Notification> getPages();
+ method public boolean getStartScrollBottom();
+ method public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
+ method public android.app.Notification.WearableExtender setContentAction(int);
+ method public android.app.Notification.WearableExtender setContentIcon(int);
+ method public android.app.Notification.WearableExtender setContentIconGravity(int);
+ method public android.app.Notification.WearableExtender setContentIntentAvailableOffline(boolean);
+ method public android.app.Notification.WearableExtender setCustomContentHeight(int);
+ method public android.app.Notification.WearableExtender setCustomSizePreset(int);
+ method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
+ method public android.app.Notification.WearableExtender setGravity(int);
+ method public android.app.Notification.WearableExtender setHintHideIcon(boolean);
+ method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
+ method public android.app.Notification.WearableExtender setStartScrollBottom(boolean);
+ field public static final int SIZE_DEFAULT = 0; // 0x0
+ field public static final int SIZE_LARGE = 4; // 0x4
+ field public static final int SIZE_MEDIUM = 3; // 0x3
+ field public static final int SIZE_SMALL = 2; // 0x2
+ field public static final int SIZE_XSMALL = 1; // 0x1
+ field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+ }
+
public class NotificationManager {
method public void cancel(int);
method public void cancel(java.lang.String, int);
@@ -5100,12 +5154,13 @@
}
public class DevicePolicyManager {
- method public void addForwardingIntentFilter(android.content.ComponentName, android.content.IntentFilter, int);
+ method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int);
method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
method public void addUserRestriction(android.content.ComponentName, java.lang.String);
- method public void clearForwardingIntentFilters(android.content.ComponentName);
+ method public void clearCrossProfileIntentFilters(android.content.ComponentName);
method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
+ method public android.os.UserHandle createUser(android.content.ComponentName, java.lang.String);
method public void enableSystemApp(android.content.ComponentName, java.lang.String);
method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
method public java.lang.String[] getAccountTypesWithManagementDisabled();
@@ -5139,6 +5194,7 @@
method public boolean isProfileOwnerApp(java.lang.String);
method public void lockNow();
method public void removeActiveAdmin(android.content.ComponentName);
+ 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);
@@ -5176,8 +5232,8 @@
field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
field public static final java.lang.String EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME = "defaultManagedProfileName";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "deviceAdminPackageName";
- field public static int FLAG_TO_MANAGED_PROFILE;
- field public static int FLAG_TO_PRIMARY_USER;
+ field public static int FLAG_MANAGED_CAN_ACCESS_PARENT;
+ field public static int FLAG_PARENT_CAN_ACCESS_MANAGED;
field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff
field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0
field public static final int KEYGUARD_DISABLE_SECURE_CAMERA = 2; // 0x2
@@ -5296,6 +5352,57 @@
package android.app.task {
+ public class Task implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getBackoffPolicy();
+ method public android.os.Bundle getExtras();
+ method public long getInitialBackoffMillis();
+ method public long getIntervalMillis();
+ method public long getMaxExecutionDelayMillis();
+ method public long getMinLatencyMillis();
+ method public int getNetworkCapabilities();
+ method public android.content.ComponentName getService();
+ method public int getTaskId();
+ method public boolean isPeriodic();
+ method public boolean isRequireCharging();
+ method public boolean isRequireDeviceIdle();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static abstract interface Task.BackoffPolicy {
+ field public static final int EXPONENTIAL = 1; // 0x1
+ field public static final int LINEAR = 0; // 0x0
+ }
+
+ public final class Task.Builder {
+ ctor public Task.Builder(int, android.content.ComponentName);
+ method public android.app.task.Task build();
+ method public android.app.task.Task.Builder setBackoffCriteria(long, int);
+ method public android.app.task.Task.Builder setExtras(android.os.Bundle);
+ method public android.app.task.Task.Builder setMinimumLatency(long);
+ method public android.app.task.Task.Builder setOverrideDeadline(long);
+ method public android.app.task.Task.Builder setPeriodic(long);
+ method public android.app.task.Task.Builder setRequiredNetworkCapabilities(int);
+ method public android.app.task.Task.Builder setRequiresCharging(boolean);
+ method public android.app.task.Task.Builder setRequiresDeviceIdle(boolean);
+ }
+
+ public static abstract interface Task.NetworkType {
+ field public static final int ANY = 0; // 0x0
+ field public static final int UNMETERED = 1; // 0x1
+ }
+
+ public abstract class TaskManager {
+ ctor public TaskManager();
+ method public abstract void cancel(int);
+ method public abstract void cancelAll();
+ method public abstract java.util.List<android.app.task.Task> getAllPendingTasks();
+ method public abstract int schedule(android.app.task.Task);
+ field public static final int RESULT_INVALID_PARAMETERS = -1; // 0xffffffff
+ field public static final int RESULT_OVER_QUOTA = -2; // 0xfffffffe
+ }
+
public class TaskParams implements android.os.Parcelable {
method public int describeContents();
method public android.os.Bundle getExtras();
@@ -5315,80 +5422,6 @@
}
-package android.app.wearable {
-
- public final class WearableActionExtensions implements android.app.Notification.Action.Builder.Extender android.os.Parcelable {
- method public android.app.Notification.Action.Builder applyTo(android.app.Notification.Action.Builder);
- method public int describeContents();
- method public static android.app.wearable.WearableActionExtensions from(android.app.Notification.Action);
- method public boolean isAvailableOffline();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- }
-
- public static final class WearableActionExtensions.Builder {
- ctor public WearableActionExtensions.Builder();
- ctor public WearableActionExtensions.Builder(android.app.wearable.WearableActionExtensions);
- method public android.app.wearable.WearableActionExtensions build();
- method public android.app.wearable.WearableActionExtensions.Builder setAvailableOffline(boolean);
- }
-
- public final class WearableNotificationExtensions implements android.app.Notification.Builder.Extender android.os.Parcelable {
- method public android.app.Notification.Builder applyTo(android.app.Notification.Builder);
- method public int describeContents();
- method public static android.app.wearable.WearableNotificationExtensions from(android.app.Notification);
- method public android.app.Notification.Action getAction(int);
- method public int getActionCount();
- method public android.app.Notification.Action[] getActions();
- method public android.graphics.Bitmap getBackground();
- method public int getContentAction();
- method public int getContentIcon();
- method public int getContentIconGravity();
- method public boolean getContentIntentAvailableOffline();
- method public int getCustomContentHeight();
- method public int getCustomSizePreset();
- method public android.app.PendingIntent getDisplayIntent();
- method public int getGravity();
- method public boolean getHintHideIcon();
- method public boolean getHintShowBackgroundOnly();
- method public android.app.Notification[] getPages();
- method public boolean getStartScrollBottom();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- field public static final int SIZE_DEFAULT = 0; // 0x0
- field public static final int SIZE_LARGE = 4; // 0x4
- field public static final int SIZE_MEDIUM = 3; // 0x3
- field public static final int SIZE_SMALL = 2; // 0x2
- field public static final int SIZE_XSMALL = 1; // 0x1
- field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
- }
-
- public static final class WearableNotificationExtensions.Builder {
- ctor public WearableNotificationExtensions.Builder();
- ctor public WearableNotificationExtensions.Builder(android.app.wearable.WearableNotificationExtensions);
- method public android.app.wearable.WearableNotificationExtensions.Builder addAction(android.app.Notification.Action);
- method public android.app.wearable.WearableNotificationExtensions.Builder addActions(java.util.List<android.app.Notification.Action>);
- method public android.app.wearable.WearableNotificationExtensions.Builder addPage(android.app.Notification);
- method public android.app.wearable.WearableNotificationExtensions.Builder addPages(java.util.List<android.app.Notification>);
- method public android.app.wearable.WearableNotificationExtensions build();
- method public android.app.wearable.WearableNotificationExtensions.Builder clearActions();
- method public android.app.wearable.WearableNotificationExtensions.Builder clearPages();
- method public android.app.wearable.WearableNotificationExtensions.Builder setBackground(android.graphics.Bitmap);
- method public android.app.wearable.WearableNotificationExtensions.Builder setContentAction(int);
- method public android.app.wearable.WearableNotificationExtensions.Builder setContentIcon(int);
- method public android.app.wearable.WearableNotificationExtensions.Builder setContentIconGravity(int);
- method public android.app.wearable.WearableNotificationExtensions.Builder setContentIntentAvailableOffline(boolean);
- method public android.app.wearable.WearableNotificationExtensions.Builder setCustomContentHeight(int);
- method public android.app.wearable.WearableNotificationExtensions.Builder setCustomSizePreset(int);
- method public android.app.wearable.WearableNotificationExtensions.Builder setDisplayIntent(android.app.PendingIntent);
- method public android.app.wearable.WearableNotificationExtensions.Builder setGravity(int);
- method public android.app.wearable.WearableNotificationExtensions.Builder setHintHideIcon(boolean);
- method public android.app.wearable.WearableNotificationExtensions.Builder setHintShowBackgroundOnly(boolean);
- method public android.app.wearable.WearableNotificationExtensions.Builder setStartScrollBottom(boolean);
- }
-
-}
-
package android.appwidget {
public class AppWidgetHost {
@@ -6993,6 +7026,7 @@
field public static final java.lang.String SEARCH_SERVICE = "search";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String STORAGE_SERVICE = "storage";
+ field public static final java.lang.String TASK_SERVICE = "task";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
field public static final java.lang.String TV_INPUT_SERVICE = "tv_input";
@@ -7931,55 +7965,6 @@
method public abstract void onStatusChanged(int);
}
- public class Task implements android.os.Parcelable {
- method public int describeContents();
- method public int getBackoffPolicy();
- method public android.os.Bundle getExtras();
- method public long getInitialBackoffMillis();
- method public long getIntervalMillis();
- method public long getMaxExecutionDelayMillis();
- method public long getMinLatencyMillis();
- method public int getNetworkCapabilities();
- method public android.content.ComponentName getService();
- method public int getTaskId();
- method public boolean isPeriodic();
- method public boolean isRequireCharging();
- method public boolean isRequireDeviceIdle();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- }
-
- public static abstract interface Task.BackoffPolicy {
- field public static final int EXPONENTIAL = 1; // 0x1
- field public static final int LINEAR = 0; // 0x0
- }
-
- public final class Task.Builder {
- ctor public Task.Builder(int, android.content.ComponentName);
- method public android.content.Task build();
- method public android.content.Task.Builder setBackoffCriteria(long, int);
- method public android.content.Task.Builder setExtras(android.os.Bundle);
- method public android.content.Task.Builder setMinimumLatency(long);
- method public android.content.Task.Builder setOverrideDeadline(long);
- method public android.content.Task.Builder setPeriodic(long);
- method public android.content.Task.Builder setRequiredNetworkCapabilities(int);
- method public android.content.Task.Builder setRequiresCharging(boolean);
- method public android.content.Task.Builder setRequiresDeviceIdle(boolean);
- }
-
- public static abstract interface Task.NetworkType {
- field public static final int ANY = 0; // 0x0
- field public static final int UNMETERED = 1; // 0x1
- }
-
- public abstract class TaskManager {
- ctor public TaskManager();
- method public abstract void cancel(int);
- method public abstract void cancelAll();
- method public abstract java.util.List<android.content.Task> getAllPendingTasks();
- method public abstract int schedule(android.content.Task);
- }
-
public class UriMatcher {
ctor public UriMatcher(int);
method public void addURI(java.lang.String, java.lang.String, int);
@@ -11952,7 +11937,6 @@
method public int getMinDelay();
method public java.lang.String getName();
method public float getPower();
- method public java.lang.String getRequiredPermission();
method public float getResolution();
method public java.lang.String getStringType();
method public int getType();
@@ -12102,6 +12086,7 @@
field public static final int SENSOR_STATUS_ACCURACY_HIGH = 3; // 0x3
field public static final int SENSOR_STATUS_ACCURACY_LOW = 1; // 0x1
field public static final int SENSOR_STATUS_ACCURACY_MEDIUM = 2; // 0x2
+ field public static final int SENSOR_STATUS_NO_CONTACT = -1; // 0xffffffff
field public static final int SENSOR_STATUS_UNRELIABLE = 0; // 0x0
field public static final deprecated int SENSOR_TEMPERATURE = 4; // 0x4
field public static final deprecated int SENSOR_TRICORDER = 64; // 0x40
@@ -12134,83 +12119,119 @@
field public static final int CAMERA_ERROR = 3; // 0x3
}
+ public abstract class CameraCaptureSession implements java.lang.AutoCloseable {
+ ctor public CameraCaptureSession();
+ method public abstract void abortCaptures() throws android.hardware.camera2.CameraAccessException;
+ method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract void close();
+ method public abstract android.hardware.camera2.CameraDevice getDevice();
+ method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+ }
+
+ public static abstract class CameraCaptureSession.CaptureListener {
+ ctor public CameraCaptureSession.CaptureListener();
+ method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
+ method public void onCaptureFailed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
+ method public void onCaptureSequenceCompleted(android.hardware.camera2.CameraDevice, int, int);
+ method public void onCaptureStarted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, long);
+ }
+
+ public static abstract class CameraCaptureSession.StateListener {
+ ctor public CameraCaptureSession.StateListener();
+ method public void onActive(android.hardware.camera2.CameraCaptureSession);
+ method public void onClosed(android.hardware.camera2.CameraCaptureSession);
+ method public abstract void onConfigureFailed(android.hardware.camera2.CameraCaptureSession);
+ method public abstract void onConfigured(android.hardware.camera2.CameraCaptureSession);
+ method public void onReady(android.hardware.camera2.CameraCaptureSession);
+ }
+
public final class CameraCharacteristics extends android.hardware.camera2.CameraMetadata {
- method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
- method public java.util.List<android.hardware.camera2.CameraMetadata.Key<?>> getAvailableCaptureRequestKeys();
- method public java.util.List<android.hardware.camera2.CameraMetadata.Key<?>> getAvailableCaptureResultKeys();
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_MODES;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_COMPENSATION_RANGE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_COMPENSATION_STEP;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_AVAILABLE_MODES;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AVAILABLE_EFFECTS;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AVAILABLE_SCENE_MODES;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_AVAILABLE_MODES;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_MAX_REGIONS;
- field public static final android.hardware.camera2.CameraMetadata.Key EDGE_AVAILABLE_EDGE_MODES;
- field public static final android.hardware.camera2.CameraMetadata.Key FLASH_INFO_AVAILABLE;
- field public static final android.hardware.camera2.CameraMetadata.Key HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
- field public static final android.hardware.camera2.CameraMetadata.Key INFO_SUPPORTED_HARDWARE_LEVEL;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_AVAILABLE_THUMBNAIL_SIZES;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_FACING;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_AVAILABLE_APERTURES;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_AVAILABLE_FILTER_DENSITIES;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_AVAILABLE_FOCAL_LENGTHS;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_FOCUS_DISTANCE_CALIBRATION;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_HYPERFOCAL_DISTANCE;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_MINIMUM_FOCUS_DISTANCE;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_SHADING_MAP_SIZE;
- field public static final android.hardware.camera2.CameraMetadata.Key NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
- field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_AVAILABLE_CAPABILITIES;
- field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_MAX_NUM_INPUT_STREAMS;
- field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_MAX_NUM_OUTPUT_STREAMS;
- field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_PARTIAL_RESULT_COUNT;
- field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_PIPELINE_MAX_DEPTH;
- field public static final android.hardware.camera2.CameraMetadata.Key SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
- field public static final android.hardware.camera2.CameraMetadata.Key SCALER_STREAM_CONFIGURATION_MAP;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_AVAILABLE_TEST_PATTERN_MODES;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_BASE_GAIN_FACTOR;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_BLACK_LEVEL_PATTERN;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_CALIBRATION_TRANSFORM1;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_CALIBRATION_TRANSFORM2;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_COLOR_TRANSFORM1;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_COLOR_TRANSFORM2;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FORWARD_MATRIX1;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FORWARD_MATRIX2;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_ACTIVE_ARRAY_SIZE;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_COLOR_FILTER_ARRANGEMENT;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_EXPOSURE_TIME_RANGE;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_MAX_FRAME_DURATION;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_PHYSICAL_SIZE;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_PIXEL_ARRAY_SIZE;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_SENSITIVITY_RANGE;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_WHITE_LEVEL;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_MAX_ANALOG_SENSITIVITY;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_ORIENTATION;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_REFERENCE_ILLUMINANT1;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_REFERENCE_ILLUMINANT2;
- field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
- field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES;
- field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_INFO_MAX_FACE_COUNT;
- field public static final android.hardware.camera2.CameraMetadata.Key SYNC_MAX_LATENCY;
- field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_AVAILABLE_TONE_MAP_MODES;
- field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MAX_CURVE_POINTS;
+ method public T get(android.hardware.camera2.CameraCharacteristics.Key<T>);
+ method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableCaptureRequestKeys();
+ method public java.util.List<android.hardware.camera2.CaptureResult.Key<?>> getAvailableCaptureResultKeys();
+ field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_AVAILABLE_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_COMPENSATION_RANGE;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_COMPENSATION_STEP;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AF_AVAILABLE_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AVAILABLE_EFFECTS;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AVAILABLE_SCENE_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AWB_AVAILABLE_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_MAX_REGIONS;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key EDGE_AVAILABLE_EDGE_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key FLASH_INFO_AVAILABLE;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key INFO_SUPPORTED_HARDWARE_LEVEL;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key JPEG_AVAILABLE_THUMBNAIL_SIZES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_FACING;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_AVAILABLE_APERTURES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_AVAILABLE_FILTER_DENSITIES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_AVAILABLE_FOCAL_LENGTHS;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_FOCUS_DISTANCE_CALIBRATION;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_HYPERFOCAL_DISTANCE;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_MINIMUM_FOCUS_DISTANCE;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_SHADING_MAP_SIZE;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_AVAILABLE_CAPABILITIES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_INPUT_STREAMS;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_OUTPUT_STREAMS;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_PARTIAL_RESULT_COUNT;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_PIPELINE_MAX_DEPTH;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SCALER_CROPPING_TYPE;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SCALER_STREAM_CONFIGURATION_MAP;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_AVAILABLE_TEST_PATTERN_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_BLACK_LEVEL_PATTERN;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_CALIBRATION_TRANSFORM1;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_CALIBRATION_TRANSFORM2;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_COLOR_TRANSFORM1;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_COLOR_TRANSFORM2;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_FORWARD_MATRIX1;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_FORWARD_MATRIX2;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_ACTIVE_ARRAY_SIZE;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_COLOR_FILTER_ARRANGEMENT;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_EXPOSURE_TIME_RANGE;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_MAX_FRAME_DURATION;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_PHYSICAL_SIZE;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_PIXEL_ARRAY_SIZE;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_SENSITIVITY_RANGE;
+ 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_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;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key STATISTICS_INFO_MAX_FACE_COUNT;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SYNC_MAX_LATENCY;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key TONEMAP_AVAILABLE_TONE_MAP_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key TONEMAP_MAX_CURVE_POINTS;
+ }
+
+ public static final class CameraCharacteristics.Key {
+ method public final boolean equals(java.lang.Object);
+ method public java.lang.String getName();
+ method public final int hashCode();
}
public abstract interface CameraDevice implements java.lang.AutoCloseable {
- method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
- method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract deprecated int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract deprecated int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void close();
- method public abstract void configureOutputs(java.util.List<android.view.Surface>) throws android.hardware.camera2.CameraAccessException;
+ method public abstract deprecated void configureOutputs(java.util.List<android.view.Surface>) throws android.hardware.camera2.CameraAccessException;
method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
- method public abstract void flush() throws android.hardware.camera2.CameraAccessException;
+ method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract deprecated void flush() throws android.hardware.camera2.CameraAccessException;
method public abstract java.lang.String getId();
- method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
- method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
- method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+ method public abstract deprecated int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract deprecated int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract deprecated void stopRepeating() throws android.hardware.camera2.CameraAccessException;
field public static final int TEMPLATE_MANUAL = 6; // 0x6
field public static final int TEMPLATE_PREVIEW = 1; // 0x1
field public static final int TEMPLATE_RECORD = 3; // 0x3
@@ -12219,7 +12240,7 @@
field public static final int TEMPLATE_ZERO_SHUTTER_LAG = 5; // 0x5
}
- public static abstract class CameraDevice.CaptureListener {
+ public static abstract deprecated class CameraDevice.CaptureListener {
ctor public CameraDevice.CaptureListener();
method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
method public void onCaptureFailed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
@@ -12229,14 +12250,14 @@
public static abstract class CameraDevice.StateListener {
ctor public CameraDevice.StateListener();
- method public void onActive(android.hardware.camera2.CameraDevice);
- method public void onBusy(android.hardware.camera2.CameraDevice);
+ method public deprecated void onActive(android.hardware.camera2.CameraDevice);
+ method public deprecated void onBusy(android.hardware.camera2.CameraDevice);
method public void onClosed(android.hardware.camera2.CameraDevice);
method public abstract void onDisconnected(android.hardware.camera2.CameraDevice);
method public abstract void onError(android.hardware.camera2.CameraDevice, int);
- method public void onIdle(android.hardware.camera2.CameraDevice);
+ method public deprecated void onIdle(android.hardware.camera2.CameraDevice);
method public abstract void onOpened(android.hardware.camera2.CameraDevice);
- method public void onUnconfigured(android.hardware.camera2.CameraDevice);
+ method public deprecated void onUnconfigured(android.hardware.camera2.CameraDevice);
field public static final int ERROR_CAMERA_DEVICE = 4; // 0x4
field public static final int ERROR_CAMERA_DISABLED = 3; // 0x3
field public static final int ERROR_CAMERA_IN_USE = 1; // 0x1
@@ -12259,8 +12280,7 @@
}
public abstract class CameraMetadata {
- method public abstract T get(android.hardware.camera2.CameraMetadata.Key<T>);
- method public java.util.List<android.hardware.camera2.CameraMetadata.Key<?>> getKeys();
+ method public java.util.List<TKey> getKeys();
field public static final int COLOR_CORRECTION_MODE_FAST = 1; // 0x1
field public static final int COLOR_CORRECTION_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int COLOR_CORRECTION_MODE_TRANSFORM_MATRIX = 0; // 0x0
@@ -12380,6 +12400,8 @@
field public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 5; // 0x5
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 2; // 0x2
field public static final int REQUEST_AVAILABLE_CAPABILITIES_ZSL = 4; // 0x4
+ field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
+ field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG = 2; // 0x2
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG = 1; // 0x1
@@ -12428,12 +12450,6 @@
field public static final int TONEMAP_MODE_HIGH_QUALITY = 2; // 0x2
}
- public static class CameraMetadata.Key {
- method public final boolean equals(java.lang.Object);
- method public final java.lang.String getName();
- method public final int hashCode();
- }
-
public class CaptureFailure {
method public int getFrameNumber();
method public int getReason();
@@ -12446,146 +12462,170 @@
public final class CaptureRequest extends android.hardware.camera2.CameraMetadata implements android.os.Parcelable {
method public int describeContents();
- method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
+ method public T get(android.hardware.camera2.CaptureRequest.Key<T>);
method public java.lang.Object getTag();
method public void writeToParcel(android.os.Parcel, int);
- field public static final android.hardware.camera2.CameraMetadata.Key BLACK_LEVEL_LOCK;
- field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_GAINS;
- field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_TRANSFORM;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_ANTIBANDING_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_EXPOSURE_COMPENSATION;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_LOCK;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_PRECAPTURE_TRIGGER;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_REGIONS;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_TARGET_FPS_RANGE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_REGIONS;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_TRIGGER;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_LOCK;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_REGIONS;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_CAPTURE_INTENT;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_EFFECT_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_SCENE_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_VIDEO_STABILIZATION_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key BLACK_LEVEL_LOCK;
+ field public static final android.hardware.camera2.CaptureRequest.Key COLOR_CORRECTION_GAINS;
+ field public static final android.hardware.camera2.CaptureRequest.Key COLOR_CORRECTION_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key COLOR_CORRECTION_TRANSFORM;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_ANTIBANDING_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_EXPOSURE_COMPENSATION;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_LOCK;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_PRECAPTURE_TRIGGER;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_REGIONS;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_TARGET_FPS_RANGE;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AF_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AF_REGIONS;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AF_TRIGGER;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AWB_LOCK;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AWB_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AWB_REGIONS;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_CAPTURE_INTENT;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_EFFECT_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_SCENE_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_VIDEO_STABILIZATION_MODE;
field public static final android.os.Parcelable.Creator CREATOR;
- field public static final android.hardware.camera2.CameraMetadata.Key EDGE_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key FLASH_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key HOT_PIXEL_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_COORDINATES;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_PROCESSING_METHOD;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_TIMESTAMP;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_ORIENTATION;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_QUALITY;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_THUMBNAIL_QUALITY;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_THUMBNAIL_SIZE;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_APERTURE;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_FILTER_DENSITY;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCAL_LENGTH;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCUS_DISTANCE;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_OPTICAL_STABILIZATION_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key NOISE_REDUCTION_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key SCALER_CROP_REGION;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_EXPOSURE_TIME;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FRAME_DURATION;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_SENSITIVITY;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_DATA;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key SHADING_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACE_DETECT_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_HOT_PIXEL_MAP_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_LENS_SHADING_MAP_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_BLUE;
- field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_GREEN;
- field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_RED;
- field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key EDGE_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key FLASH_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key HOT_PIXEL_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key JPEG_GPS_COORDINATES;
+ field public static final android.hardware.camera2.CaptureRequest.Key JPEG_GPS_PROCESSING_METHOD;
+ field public static final android.hardware.camera2.CaptureRequest.Key JPEG_GPS_TIMESTAMP;
+ field public static final android.hardware.camera2.CaptureRequest.Key JPEG_ORIENTATION;
+ field public static final android.hardware.camera2.CaptureRequest.Key JPEG_QUALITY;
+ field public static final android.hardware.camera2.CaptureRequest.Key JPEG_THUMBNAIL_QUALITY;
+ field public static final android.hardware.camera2.CaptureRequest.Key JPEG_THUMBNAIL_SIZE;
+ field public static final android.hardware.camera2.CaptureRequest.Key LENS_APERTURE;
+ field public static final android.hardware.camera2.CaptureRequest.Key LENS_FILTER_DENSITY;
+ field public static final android.hardware.camera2.CaptureRequest.Key LENS_FOCAL_LENGTH;
+ field public static final android.hardware.camera2.CaptureRequest.Key LENS_FOCUS_DISTANCE;
+ field public static final android.hardware.camera2.CaptureRequest.Key LENS_OPTICAL_STABILIZATION_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key NOISE_REDUCTION_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key SCALER_CROP_REGION;
+ field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_EXPOSURE_TIME;
+ field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_FRAME_DURATION;
+ field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_SENSITIVITY;
+ field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_TEST_PATTERN_DATA;
+ field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_TEST_PATTERN_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key SHADING_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key STATISTICS_FACE_DETECT_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key STATISTICS_HOT_PIXEL_MAP_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key STATISTICS_LENS_SHADING_MAP_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_CURVE_BLUE;
+ field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_CURVE_GREEN;
+ field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_CURVE_RED;
+ field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_MODE;
}
public static final class CaptureRequest.Builder {
method public void addTarget(android.view.Surface);
method public android.hardware.camera2.CaptureRequest build();
- method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
+ method public T get(android.hardware.camera2.CaptureRequest.Key<T>);
method public void removeTarget(android.view.Surface);
- method public void set(android.hardware.camera2.CameraMetadata.Key<T>, T);
+ method public void set(android.hardware.camera2.CaptureRequest.Key<T>, T);
method public void setTag(java.lang.Object);
}
+ public static final class CaptureRequest.Key {
+ method public final boolean equals(java.lang.Object);
+ method public java.lang.String getName();
+ method public final int hashCode();
+ }
+
public final class CaptureResult extends android.hardware.camera2.CameraMetadata {
- method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
+ method public T get(android.hardware.camera2.CaptureResult.Key<T>);
method public int getFrameNumber();
method public android.hardware.camera2.CaptureRequest getRequest();
method public int getSequenceId();
- field public static final android.hardware.camera2.CameraMetadata.Key BLACK_LEVEL_LOCK;
- field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_GAINS;
- field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_TRANSFORM;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_ANTIBANDING_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_EXPOSURE_COMPENSATION;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_LOCK;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_PRECAPTURE_TRIGGER;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_REGIONS;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_STATE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_TARGET_FPS_RANGE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_REGIONS;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_STATE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_TRIGGER;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_LOCK;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_REGIONS;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_STATE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_CAPTURE_INTENT;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_EFFECT_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_SCENE_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_VIDEO_STABILIZATION_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key EDGE_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key FLASH_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key FLASH_STATE;
- field public static final android.hardware.camera2.CameraMetadata.Key HOT_PIXEL_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_COORDINATES;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_PROCESSING_METHOD;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_TIMESTAMP;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_ORIENTATION;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_QUALITY;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_THUMBNAIL_QUALITY;
- field public static final android.hardware.camera2.CameraMetadata.Key JPEG_THUMBNAIL_SIZE;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_APERTURE;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_FILTER_DENSITY;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCAL_LENGTH;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCUS_DISTANCE;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCUS_RANGE;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_OPTICAL_STABILIZATION_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key LENS_STATE;
- field public static final android.hardware.camera2.CameraMetadata.Key NOISE_REDUCTION_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_FRAME_COUNT;
- field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_PIPELINE_DEPTH;
- field public static final android.hardware.camera2.CameraMetadata.Key SCALER_CROP_REGION;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_EXPOSURE_TIME;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FRAME_DURATION;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_GREEN_SPLIT;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_NEUTRAL_COLOR_POINT;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_SENSITIVITY;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEMPERATURE;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_DATA;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TIMESTAMP;
- field public static final android.hardware.camera2.CameraMetadata.Key SHADING_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACES;
- field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACE_DETECT_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_HOT_PIXEL_MAP;
- field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_HOT_PIXEL_MAP_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_LENS_SHADING_MAP;
- field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_LENS_SHADING_MAP_MODE;
- field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_SCENE_FLICKER;
- field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_BLUE;
- field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_GREEN;
- field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_RED;
- field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key BLACK_LEVEL_LOCK;
+ field public static final android.hardware.camera2.CaptureResult.Key COLOR_CORRECTION_GAINS;
+ field public static final android.hardware.camera2.CaptureResult.Key COLOR_CORRECTION_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key COLOR_CORRECTION_TRANSFORM;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_ANTIBANDING_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_EXPOSURE_COMPENSATION;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_LOCK;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_PRECAPTURE_TRIGGER;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_REGIONS;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_STATE;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_TARGET_FPS_RANGE;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AF_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AF_REGIONS;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AF_STATE;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AF_TRIGGER;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AWB_LOCK;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AWB_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AWB_REGIONS;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AWB_STATE;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_CAPTURE_INTENT;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_EFFECT_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_SCENE_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key CONTROL_VIDEO_STABILIZATION_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key EDGE_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key FLASH_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key FLASH_STATE;
+ field public static final android.hardware.camera2.CaptureResult.Key HOT_PIXEL_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key JPEG_GPS_COORDINATES;
+ field public static final android.hardware.camera2.CaptureResult.Key JPEG_GPS_PROCESSING_METHOD;
+ field public static final android.hardware.camera2.CaptureResult.Key JPEG_GPS_TIMESTAMP;
+ field public static final android.hardware.camera2.CaptureResult.Key JPEG_ORIENTATION;
+ field public static final android.hardware.camera2.CaptureResult.Key JPEG_QUALITY;
+ field public static final android.hardware.camera2.CaptureResult.Key JPEG_THUMBNAIL_QUALITY;
+ field public static final android.hardware.camera2.CaptureResult.Key JPEG_THUMBNAIL_SIZE;
+ field public static final android.hardware.camera2.CaptureResult.Key LENS_APERTURE;
+ field public static final android.hardware.camera2.CaptureResult.Key LENS_FILTER_DENSITY;
+ field public static final android.hardware.camera2.CaptureResult.Key LENS_FOCAL_LENGTH;
+ field public static final android.hardware.camera2.CaptureResult.Key LENS_FOCUS_DISTANCE;
+ field public static final android.hardware.camera2.CaptureResult.Key LENS_FOCUS_RANGE;
+ field public static final android.hardware.camera2.CaptureResult.Key LENS_OPTICAL_STABILIZATION_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key LENS_STATE;
+ field public static final android.hardware.camera2.CaptureResult.Key NOISE_REDUCTION_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key REQUEST_FRAME_COUNT;
+ field public static final android.hardware.camera2.CaptureResult.Key REQUEST_PIPELINE_DEPTH;
+ field public static final android.hardware.camera2.CaptureResult.Key SCALER_CROP_REGION;
+ field public static final android.hardware.camera2.CaptureResult.Key SENSOR_EXPOSURE_TIME;
+ 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_SENSITIVITY;
+ field public static final android.hardware.camera2.CaptureResult.Key SENSOR_TEST_PATTERN_DATA;
+ field public static final android.hardware.camera2.CaptureResult.Key SENSOR_TEST_PATTERN_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key SENSOR_TIMESTAMP;
+ field public static final android.hardware.camera2.CaptureResult.Key SHADING_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_FACES;
+ field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_FACE_DETECT_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_HOT_PIXEL_MAP;
+ field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_HOT_PIXEL_MAP_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_LENS_SHADING_MAP;
+ field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_LENS_SHADING_MAP_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_SCENE_FLICKER;
+ field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE_BLUE;
+ field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE_GREEN;
+ field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE_RED;
+ field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_MODE;
+ }
+
+ public static final class CaptureResult.Key {
+ method public final boolean equals(java.lang.Object);
+ method public java.lang.String getName();
+ method public final int hashCode();
+ }
+
+ public final class DngCreator implements java.lang.AutoCloseable {
+ ctor public DngCreator(android.hardware.camera2.CameraCharacteristics, android.hardware.camera2.CaptureResult);
+ method public void close();
+ method public android.hardware.camera2.DngCreator setDescription(java.lang.String);
+ method public android.hardware.camera2.DngCreator setLocation(android.location.Location);
+ method public android.hardware.camera2.DngCreator setOrientation(int);
+ method public android.hardware.camera2.DngCreator setThumbnail(android.graphics.Bitmap);
+ method public android.hardware.camera2.DngCreator setThumbnail(android.media.Image);
+ method public void writeByteBuffer(java.io.OutputStream, android.util.Size, java.nio.ByteBuffer, long) throws java.io.IOException;
+ method public void writeImage(java.io.OutputStream, android.media.Image) throws java.io.IOException;
+ method public void writeInputStream(java.io.OutputStream, android.util.Size, java.io.InputStream, long) throws java.io.IOException;
}
}
@@ -13724,7 +13764,6 @@
}
public class AudioFormat {
- ctor public AudioFormat();
field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1
field public static final deprecated int CHANNEL_CONFIGURATION_INVALID = 0; // 0x0
field public static final deprecated int CHANNEL_CONFIGURATION_MONO = 2; // 0x2
@@ -14056,19 +14095,6 @@
ctor public DeniedByServerException(java.lang.String);
}
- public final class DngCreator implements java.lang.AutoCloseable {
- ctor public DngCreator(android.hardware.camera2.CameraCharacteristics, android.hardware.camera2.CaptureResult);
- method public void close();
- method public android.media.DngCreator setDescription(java.lang.String);
- method public android.media.DngCreator setLocation(android.location.Location);
- method public android.media.DngCreator setOrientation(int);
- method public android.media.DngCreator setThumbnail(android.graphics.Bitmap);
- method public android.media.DngCreator setThumbnail(android.media.Image);
- method public void writeByteBuffer(java.io.OutputStream, android.util.Size, java.nio.ByteBuffer, long) throws java.io.IOException;
- method public void writeImage(java.io.OutputStream, android.media.Image) throws java.io.IOException;
- method public void writeInputStream(java.io.OutputStream, android.util.Size, java.io.InputStream, long) throws java.io.IOException;
- }
-
public class ExifInterface {
ctor public ExifInterface(java.lang.String) throws java.io.IOException;
method public double getAltitude(double);
@@ -17088,11 +17114,9 @@
}
public static final class WifiEnterpriseConfig.Eap {
- field public static final int AKA = 5; // 0x5
field public static final int NONE = -1; // 0xffffffff
field public static final int PEAP = 0; // 0x0
field public static final int PWD = 3; // 0x3
- field public static final int SIM = 4; // 0x4
field public static final int TLS = 1; // 0x1
field public static final int TTLS = 2; // 0x2
}
@@ -17512,14 +17536,14 @@
package android.net.wifi.passpoint {
public class WifiPasspointCredential implements android.os.Parcelable {
- ctor public WifiPasspointCredential(java.lang.String, android.net.wifi.WifiEnterpriseConfig);
+ ctor public WifiPasspointCredential(java.lang.String, java.lang.String, android.net.wifi.WifiEnterpriseConfig);
method public int describeContents();
- method public java.lang.String getClientCertPath();
- method public int getEapMethod();
+ method public android.net.wifi.WifiEnterpriseConfig getEnterpriseConfig();
method public java.lang.String getFqdn();
- method public java.lang.String getImsi();
method public java.lang.String getRealm();
- method public java.lang.String getUserName();
+ method public void setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+ method public void setFqdn(java.lang.String);
+ method public void setRealm(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
}
@@ -25026,7 +25050,9 @@
field public static final java.lang.String COLUMN_DESCRIPTION = "description";
field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number";
+ field public static final java.lang.String COLUMN_LOCKED = "locked";
field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
+ field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
field public static final java.lang.String COLUMN_SERVICE_NAME = "service_name";
field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
@@ -25064,10 +25090,12 @@
}
public static final class TvContract.Programs implements android.provider.TvContract.BaseTvColumns {
+ field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
field public static final java.lang.String COLUMN_DATA = "data";
field public static final java.lang.String COLUMN_DESCRIPTION = "description";
field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+ field public static final java.lang.String COLUMN_GENRE = "genre";
field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_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_TITLE = "title";
@@ -28178,12 +28206,8 @@
method public java.lang.String getVoiceMailAlphaTag();
method public java.lang.String getVoiceMailNumber();
method public boolean hasIccCard();
- method public boolean iccCloseLogicalChannel(int);
- method public int iccOpenLogicalChannel(java.lang.String);
- method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
method public boolean isNetworkRoaming();
method public void listen(android.telephony.PhoneStateListener, int);
- method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
field public static final int CALL_STATE_IDLE = 0; // 0x0
@@ -32869,6 +32893,7 @@
method public void requestLayout();
method public boolean requestRectangleOnScreen(android.graphics.Rect);
method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
+ method public final void requestUnbufferedDispatch(android.view.MotionEvent);
method public static int resolveSize(int, int);
method public static int resolveSizeAndState(int, int, int);
method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>);
@@ -36562,6 +36587,10 @@
method public void setRowCount(int);
method public void setRowOrderPreserved(boolean);
method public void setUseDefaultMargins(boolean);
+ method public static android.widget.GridLayout.Spec spec(int, int, android.widget.GridLayout.Alignment, float);
+ method public static android.widget.GridLayout.Spec spec(int, android.widget.GridLayout.Alignment, float);
+ method public static android.widget.GridLayout.Spec spec(int, int, float);
+ method public static android.widget.GridLayout.Spec spec(int, float);
method public static android.widget.GridLayout.Spec spec(int, int, android.widget.GridLayout.Alignment);
method public static android.widget.GridLayout.Spec spec(int, android.widget.GridLayout.Alignment);
method public static android.widget.GridLayout.Spec spec(int, int);
diff --git a/api/removed.txt b/api/removed.txt
index e69de29..458c422 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -0,0 +1,8 @@
+package android.media {
+
+ public class AudioFormat {
+ ctor public AudioFormat();
+ }
+
+}
+
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 5454b46..b7c2c22 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1146,12 +1146,22 @@
}
private void runUninstall() {
- int unInstallFlags = PackageManager.DELETE_ALL_USERS;
+ int unInstallFlags = 0;
+ int userId = UserHandle.USER_ALL;
String opt;
while ((opt=nextOption()) != null) {
if (opt.equals("-k")) {
unInstallFlags |= PackageManager.DELETE_KEEP_DATA;
+ } else if (opt.equals("--user")) {
+ String param = nextArg();
+ if (isNumber(param)) {
+ userId = Integer.parseInt(param);
+ } else {
+ showUsage();
+ System.err.println("Error: Invalid user: " + param);
+ return;
+ }
} else {
System.err.println("Error: Unknown option: " + opt);
return;
@@ -1164,7 +1174,34 @@
showUsage();
return;
}
- boolean result = deletePackage(pkg, unInstallFlags);
+
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_OWNER;
+ unInstallFlags |= PackageManager.DELETE_ALL_USERS;
+ } else {
+ PackageInfo info;
+ try {
+ info = mPm.getPackageInfo(pkg, 0, userId);
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ return;
+ }
+ if (info == null) {
+ System.err.println("Failure - not installed for " + userId);
+ return;
+ }
+ final boolean isSystem =
+ (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ // If we are being asked to delete a system app for just one
+ // user set flag so it disables rather than reverting to system
+ // version of the app.
+ if (isSystem) {
+ unInstallFlags |= PackageManager.DELETE_SYSTEM_APP;
+ }
+ }
+
+ boolean result = deletePackage(pkg, unInstallFlags, userId);
if (result) {
System.out.println("Success");
} else {
@@ -1172,10 +1209,10 @@
}
}
- private boolean deletePackage(String pkg, int unInstallFlags) {
+ private boolean deletePackage(String pkg, int unInstallFlags, int userId) {
PackageDeleteObserver obs = new PackageDeleteObserver();
try {
- mPm.deletePackageAsUser(pkg, obs, UserHandle.USER_OWNER, unInstallFlags);
+ mPm.deletePackageAsUser(pkg, obs, userId, unInstallFlags);
synchronized (obs) {
while (!obs.finished) {
@@ -1571,7 +1608,7 @@
System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f]");
System.err.println(" [--algo <algorithm name> --key <key-in-hex> --iv <IV-in-hex>]");
System.err.println(" [--originating-uri <URI>] [--referrer <URI>] PATH");
- System.err.println(" pm uninstall [-k] PACKAGE");
+ System.err.println(" pm uninstall [-k] [--user USER_ID] PACKAGE");
System.err.println(" pm clear [--user USER_ID] PACKAGE");
System.err.println(" pm enable [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
@@ -1585,7 +1622,7 @@
System.err.println(" pm get-install-location");
System.err.println(" pm set-permission-enforced PERMISSION [true|false]");
System.err.println(" pm trim-caches DESIRED_FREE_SPACE");
- System.err.println(" pm create-user [--relatedTo USER_ID] [--managed] USER_NAME");
+ System.err.println(" pm create-user [--profileOf USER_ID] [--managed] USER_NAME");
System.err.println(" pm remove-user USER_ID");
System.err.println(" pm get-max-users");
System.err.println("");
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5fd288f..d9adba3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3010,8 +3010,8 @@
int h;
if (w < 0) {
Resources res = r.activity.getResources();
- int wId = com.android.internal.R.dimen.recents_thumbnail_width;
- int hId = com.android.internal.R.dimen.recents_thumbnail_height;
+ int wId = com.android.internal.R.dimen.thumbnail_width;
+ int hId = com.android.internal.R.dimen.thumbnail_height;
mThumbnailWidth = w = res.getDimensionPixelSize(wId);
mThumbnailHeight = h = res.getDimensionPixelSize(hId);
} else {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 2f35160..84673d9 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1455,10 +1455,10 @@
* @hide
*/
@Override
- public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int userIdOrig,
- int userIdDest) {
+ public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable,
+ int sourceUserId, int targetUserId) {
try {
- mPM.addForwardingIntentFilter(filter, removable, userIdOrig, userIdDest);
+ mPM.addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId);
} catch (RemoteException e) {
// Should never happen!
}
@@ -1468,14 +1468,31 @@
* @hide
*/
@Override
- public void clearForwardingIntentFilters(int userIdOrig) {
+ public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int sourceUserId,
+ int targetUserId) {
+ addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void clearCrossProfileIntentFilters(int sourceUserId) {
try {
- mPM.clearForwardingIntentFilters(userIdOrig);
+ mPM.clearCrossProfileIntentFilters(sourceUserId);
} catch (RemoteException e) {
// Should never happen!
}
}
+ /**
+ * @hide
+ */
+ @Override
+ public void clearForwardingIntentFilters(int sourceUserId) {
+ clearCrossProfileIntentFilters(sourceUserId);
+ }
+
private final ContextImpl mContext;
private final IPackageManager mPM;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1634d11..ff8688d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -133,10 +133,12 @@
import android.accounts.AccountManager;
import android.accounts.IAccountManager;
import android.app.admin.DevicePolicyManager;
+import android.app.task.ITaskManager;
import android.app.trust.TrustManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.appwidget.IAppWidgetService.Stub;
import com.android.internal.os.IDropBoxManagerService;
import java.io.File;
@@ -693,6 +695,12 @@
public Object createService(ContextImpl ctx) {
return new UsageStatsManager(ctx.getOuterContext());
}});
+
+ registerService(TASK_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(TASK_SERVICE);
+ return new TaskManagerImpl(ITaskManager.Stub.asInterface(b));
+ }});
}
static ContextImpl getImpl(Context context) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index dfd927f..6e23b11 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -35,6 +35,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
+import android.view.Gravity;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.RemoteViews;
@@ -46,7 +47,9 @@
import java.lang.annotation.RetentionPolicy;
import java.text.NumberFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
/**
* A class that represents how a persistent notification is to be presented to
@@ -767,7 +770,7 @@
*/
public static class Action implements Parcelable {
private final Bundle mExtras;
- private RemoteInput[] mRemoteInputs;
+ private final RemoteInput[] mRemoteInputs;
/**
* Small icon representing the action.
@@ -910,25 +913,12 @@
* Apply an extender to this action builder. Extenders may be used to add
* metadata or change options on this builder.
*/
- public Builder apply(Extender extender) {
- extender.applyTo(this);
+ public Builder extend(Extender extender) {
+ extender.extend(this);
return this;
}
/**
- * Extender interface for use with {@link #apply}. Extenders may be used to add
- * metadata or change options on this builder.
- */
- public interface Extender {
- /**
- * Apply this extender to a notification action builder.
- * @param builder the builder to be modified.
- * @return the build object for chaining.
- */
- public Builder applyTo(Builder builder);
- }
-
- /**
* Combine all of the options that have been set and return a new {@link Action}
* object.
* @return the built action
@@ -975,6 +965,121 @@
return new Action[size];
}
};
+
+ /**
+ * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
+ * metadata or change options on an action builder.
+ */
+ public interface Extender {
+ /**
+ * Apply this extender to a notification action builder.
+ * @param builder the builder to be modified.
+ * @return the build object for chaining.
+ */
+ public Builder extend(Builder builder);
+ }
+
+ /**
+ * Wearable extender for notification actions. To add extensions to an action,
+ * create a new {@link android.app.Notification.Action.WearableExtender} object using
+ * the {@code WearableExtender()} constructor and apply it to a
+ * {@link android.app.Notification.Action.Builder} using
+ * {@link android.app.Notification.Action.Builder#extend}.
+ *
+ * <pre class="prettyprint">
+ * Notification.Action action = new Notification.Action.Builder(
+ * R.drawable.archive_all, "Archive all", actionIntent)
+ * .apply(new Notification.Action.WearableExtender()
+ * .setAvailableOffline(false))
+ * .build();
+ * </pre>
+ */
+ public static final class WearableExtender implements Extender {
+ /** Notification action extra which contains wearable extensions */
+ private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
+
+ private static final String KEY_FLAGS = "flags";
+
+ // Flags bitwise-ored to mFlags
+ private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
+
+ // Default value for flags integer
+ private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
+
+ private int mFlags = DEFAULT_FLAGS;
+
+ /**
+ * Create a {@link android.app.Notification.Action.WearableExtender} with default
+ * options.
+ */
+ public WearableExtender() {
+ }
+
+ /**
+ * Create a {@link android.app.Notification.Action.WearableExtender} by reading
+ * wearable options present in an existing notification action.
+ * @param action the notification action to inspect.
+ */
+ public WearableExtender(Action action) {
+ Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
+ if (wearableBundle != null) {
+ mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
+ }
+ }
+
+ /**
+ * Apply wearable extensions to a notification action that is being built. This is
+ * typically called by the {@link android.app.Notification.Action.Builder#extend}
+ * method of {@link android.app.Notification.Action.Builder}.
+ */
+ @Override
+ public Action.Builder extend(Action.Builder builder) {
+ Bundle wearableBundle = new Bundle();
+
+ if (mFlags != DEFAULT_FLAGS) {
+ wearableBundle.putInt(KEY_FLAGS, mFlags);
+ }
+
+ builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
+ return builder;
+ }
+
+ @Override
+ public WearableExtender clone() {
+ WearableExtender that = new WearableExtender();
+ that.mFlags = this.mFlags;
+ return that;
+ }
+
+ /**
+ * Set whether this action is available when the wearable device is not connected to
+ * a companion device. The user can still trigger this action when the wearable device is
+ * offline, but a visual hint will indicate that the action may not be available.
+ * Defaults to true.
+ */
+ public WearableExtender setAvailableOffline(boolean availableOffline) {
+ setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
+ return this;
+ }
+
+ /**
+ * Get whether this action is available when the wearable device is not connected to
+ * a companion device. The user can still trigger this action when the wearable device is
+ * offline, but a visual hint will indicate that the action may not be available.
+ * Defaults to true.
+ */
+ public boolean isAvailableOffline() {
+ return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
+ }
+
+ private void setFlag(int mask, boolean value) {
+ if (value) {
+ mFlags |= mask;
+ } else {
+ mFlags &= ~mask;
+ }
+ }
+ }
}
/**
@@ -2169,24 +2274,11 @@
* Apply an extender to this notification builder. Extenders may be used to add
* metadata or change options on this builder.
*/
- public Builder apply(Extender extender) {
- extender.applyTo(this);
+ public Builder extend(Extender extender) {
+ extender.extend(this);
return this;
}
- /**
- * Extender interface for use with {@link #apply}. Extenders may be used to add
- * metadata or change options on this builder.
- */
- public interface Extender {
- /**
- * Apply this extender to a notification builder.
- * @param builder the builder to be modified.
- * @return the build object for chaining.
- */
- public Builder applyTo(Builder builder);
- }
-
private void setFlag(int mask, boolean value) {
if (value) {
mFlags |= mask;
@@ -3163,4 +3255,634 @@
return big;
}
}
+
+ /**
+ * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
+ * metadata or change options on a notification builder.
+ */
+ public interface Extender {
+ /**
+ * Apply this extender to a notification builder.
+ * @param builder the builder to be modified.
+ * @return the build object for chaining.
+ */
+ public Builder extend(Builder builder);
+ }
+
+ /**
+ * Helper class to add wearable extensions to notifications.
+ * <p class="note"> See
+ * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
+ * for Android Wear</a> for more information on how to use this class.
+ * <p>
+ * To create a notification with wearable extensions:
+ * <ol>
+ * <li>Create a {@link android.app.Notification.Builder}, setting any desired
+ * properties.
+ * <li>Create a {@link android.app.Notification.WearableExtender}.
+ * <li>Set wearable-specific properties using the
+ * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
+ * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
+ * notification.
+ * <li>Post the notification to the notification system with the
+ * {@code NotificationManager.notify(...)} methods.
+ * </ol>
+ *
+ * <pre class="prettyprint">
+ * Notification notif = new Notification.Builder(mContext)
+ * .setContentTitle("New mail from " + sender.toString())
+ * .setContentText(subject)
+ * .setSmallIcon(R.drawable.new_mail)
+ * .extend(new Notification.WearableExtender()
+ * .setContentIcon(R.drawable.new_mail))
+ * .build();
+ * NotificationManager notificationManger =
+ * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ * notificationManger.notify(0, notif);</pre>
+ *
+ * <p>Wearable extensions can be accessed on an existing notification by using the
+ * {@code WearableExtender(Notification)} constructor,
+ * and then using the {@code get} methods to access values.
+ *
+ * <pre class="prettyprint">
+ * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
+ * notification);
+ * List<Notification> pages = wearableExtender.getPages();
+ * </pre>
+ */
+ public static final class WearableExtender implements Extender {
+ /**
+ * Sentinel value for an action index that is unset.
+ */
+ public static final int UNSET_ACTION_INDEX = -1;
+
+ /**
+ * Size value for use with {@link #setCustomSizePreset} to show this notification with
+ * default sizing.
+ * <p>For custom display notifications created using {@link #setDisplayIntent},
+ * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
+ * on their content.
+ */
+ public static final int SIZE_DEFAULT = 0;
+
+ /**
+ * Size value for use with {@link #setCustomSizePreset} to show this notification
+ * with an extra small size.
+ * <p>This value is only applicable for custom display notifications created using
+ * {@link #setDisplayIntent}.
+ */
+ public static final int SIZE_XSMALL = 1;
+
+ /**
+ * Size value for use with {@link #setCustomSizePreset} to show this notification
+ * with a small size.
+ * <p>This value is only applicable for custom display notifications created using
+ * {@link #setDisplayIntent}.
+ */
+ public static final int SIZE_SMALL = 2;
+
+ /**
+ * Size value for use with {@link #setCustomSizePreset} to show this notification
+ * with a medium size.
+ * <p>This value is only applicable for custom display notifications created using
+ * {@link #setDisplayIntent}.
+ */
+ public static final int SIZE_MEDIUM = 3;
+
+ /**
+ * Size value for use with {@link #setCustomSizePreset} to show this notification
+ * with a large size.
+ * <p>This value is only applicable for custom display notifications created using
+ * {@link #setDisplayIntent}.
+ */
+ public static final int SIZE_LARGE = 4;
+
+ /** Notification extra which contains wearable extensions */
+ private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
+
+ // Keys within EXTRA_WEARABLE_OPTIONS for wearable options.
+ private static final String KEY_ACTIONS = "actions";
+ private static final String KEY_FLAGS = "flags";
+ private static final String KEY_DISPLAY_INTENT = "displayIntent";
+ private static final String KEY_PAGES = "pages";
+ private static final String KEY_BACKGROUND = "background";
+ private static final String KEY_CONTENT_ICON = "contentIcon";
+ private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
+ private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
+ private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
+ private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
+ private static final String KEY_GRAVITY = "gravity";
+
+ // Flags bitwise-ored to mFlags
+ private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
+ private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
+ private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
+ private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
+
+ // Default value for flags integer
+ private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
+
+ private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
+ private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
+
+ private ArrayList<Action> mActions = new ArrayList<Action>();
+ private int mFlags = DEFAULT_FLAGS;
+ private PendingIntent mDisplayIntent;
+ private ArrayList<Notification> mPages = new ArrayList<Notification>();
+ private Bitmap mBackground;
+ private int mContentIcon;
+ private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
+ private int mContentActionIndex = UNSET_ACTION_INDEX;
+ private int mCustomSizePreset = SIZE_DEFAULT;
+ private int mCustomContentHeight;
+ private int mGravity = DEFAULT_GRAVITY;
+
+ /**
+ * Create a {@link android.app.Notification.WearableExtender} with default
+ * options.
+ */
+ public WearableExtender() {
+ }
+
+ public WearableExtender(Notification notif) {
+ Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
+ if (wearableBundle != null) {
+ List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
+ if (actions != null) {
+ mActions.addAll(actions);
+ }
+
+ mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
+ mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
+
+ Notification[] pages = getNotificationArrayFromBundle(
+ wearableBundle, KEY_PAGES);
+ if (pages != null) {
+ Collections.addAll(mPages, pages);
+ }
+
+ mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
+ mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
+ mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
+ DEFAULT_CONTENT_ICON_GRAVITY);
+ mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
+ UNSET_ACTION_INDEX);
+ mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
+ SIZE_DEFAULT);
+ mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
+ mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
+ }
+ }
+
+ /**
+ * Apply wearable extensions to a notification that is being built. This is typically
+ * called by the {@link android.app.Notification.Builder#extend} method of
+ * {@link android.app.Notification.Builder}.
+ */
+ @Override
+ public Notification.Builder extend(Notification.Builder builder) {
+ Bundle wearableBundle = new Bundle();
+
+ if (!mActions.isEmpty()) {
+ wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
+ }
+ if (mFlags != DEFAULT_FLAGS) {
+ wearableBundle.putInt(KEY_FLAGS, mFlags);
+ }
+ if (mDisplayIntent != null) {
+ wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
+ }
+ if (!mPages.isEmpty()) {
+ wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
+ new Notification[mPages.size()]));
+ }
+ if (mBackground != null) {
+ wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
+ }
+ if (mContentIcon != 0) {
+ wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
+ }
+ if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
+ wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
+ }
+ if (mContentActionIndex != UNSET_ACTION_INDEX) {
+ wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
+ mContentActionIndex);
+ }
+ if (mCustomSizePreset != SIZE_DEFAULT) {
+ wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
+ }
+ if (mCustomContentHeight != 0) {
+ wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
+ }
+ if (mGravity != DEFAULT_GRAVITY) {
+ wearableBundle.putInt(KEY_GRAVITY, mGravity);
+ }
+
+ builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
+ return builder;
+ }
+
+ @Override
+ public WearableExtender clone() {
+ WearableExtender that = new WearableExtender();
+ that.mActions = new ArrayList<Action>(this.mActions);
+ that.mFlags = this.mFlags;
+ that.mDisplayIntent = this.mDisplayIntent;
+ that.mPages = new ArrayList<Notification>(this.mPages);
+ that.mBackground = this.mBackground;
+ that.mContentIcon = this.mContentIcon;
+ that.mContentIconGravity = this.mContentIconGravity;
+ that.mContentActionIndex = this.mContentActionIndex;
+ that.mCustomSizePreset = this.mCustomSizePreset;
+ that.mCustomContentHeight = this.mCustomContentHeight;
+ that.mGravity = this.mGravity;
+ return that;
+ }
+
+ /**
+ * Add a wearable action to this notification.
+ *
+ * <p>When wearable actions are added using this method, the set of actions that
+ * show on a wearable device splits from devices that only show actions added
+ * using {@link android.app.Notification.Builder#addAction}. This allows for customization
+ * of which actions display on different devices.
+ *
+ * @param action the action to add to this notification
+ * @return this object for method chaining
+ * @see android.app.Notification.Action
+ */
+ public WearableExtender addAction(Action action) {
+ mActions.add(action);
+ return this;
+ }
+
+ /**
+ * Adds wearable actions to this notification.
+ *
+ * <p>When wearable actions are added using this method, the set of actions that
+ * show on a wearable device splits from devices that only show actions added
+ * using {@link android.app.Notification.Builder#addAction}. This allows for customization
+ * of which actions display on different devices.
+ *
+ * @param actions the actions to add to this notification
+ * @return this object for method chaining
+ * @see android.app.Notification.Action
+ */
+ public WearableExtender addActions(List<Action> actions) {
+ mActions.addAll(actions);
+ return this;
+ }
+
+ /**
+ * Clear all wearable actions present on this builder.
+ * @return this object for method chaining.
+ * @see #addAction
+ */
+ public WearableExtender clearActions() {
+ mActions.clear();
+ return this;
+ }
+
+ /**
+ * Get the wearable actions present on this notification.
+ */
+ public List<Action> getActions() {
+ return mActions;
+ }
+
+ /**
+ * Set an intent to launch inside of an activity view when displaying
+ * this notification. This {@link PendingIntent} should be for an activity.
+ *
+ * @param intent the {@link PendingIntent} for an activity
+ * @return this object for method chaining
+ * @see android.app.Notification.WearableExtender#getDisplayIntent
+ */
+ public WearableExtender setDisplayIntent(PendingIntent intent) {
+ mDisplayIntent = intent;
+ return this;
+ }
+
+ /**
+ * Get the intent to launch inside of an activity view when displaying this
+ * notification. This {@code PendingIntent} should be for an activity.
+ */
+ public PendingIntent getDisplayIntent() {
+ return mDisplayIntent;
+ }
+
+ /**
+ * Add an additional page of content to display with this notification. The current
+ * notification forms the first page, and pages added using this function form
+ * subsequent pages. This field can be used to separate a notification into multiple
+ * sections.
+ *
+ * @param page the notification to add as another page
+ * @return this object for method chaining
+ * @see android.app.Notification.WearableExtender#getPages
+ */
+ public WearableExtender addPage(Notification page) {
+ mPages.add(page);
+ return this;
+ }
+
+ /**
+ * Add additional pages of content to display with this notification. The current
+ * notification forms the first page, and pages added using this function form
+ * subsequent pages. This field can be used to separate a notification into multiple
+ * sections.
+ *
+ * @param pages a list of notifications
+ * @return this object for method chaining
+ * @see android.app.Notification.WearableExtender#getPages
+ */
+ public WearableExtender addPages(List<Notification> pages) {
+ mPages.addAll(pages);
+ return this;
+ }
+
+ /**
+ * Clear all additional pages present on this builder.
+ * @return this object for method chaining.
+ * @see #addPage
+ */
+ public WearableExtender clearPages() {
+ mPages.clear();
+ return this;
+ }
+
+ /**
+ * Get the array of additional pages of content for displaying this notification. The
+ * current notification forms the first page, and elements within this array form
+ * subsequent pages. This field can be used to separate a notification into multiple
+ * sections.
+ * @return the pages for this notification
+ */
+ public List<Notification> getPages() {
+ return mPages;
+ }
+
+ /**
+ * Set a background image to be displayed behind the notification content.
+ * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
+ * will work with any notification style.
+ *
+ * @param background the background bitmap
+ * @return this object for method chaining
+ * @see android.app.Notification.WearableExtender#getBackground
+ */
+ public WearableExtender setBackground(Bitmap background) {
+ mBackground = background;
+ return this;
+ }
+
+ /**
+ * Get a background image to be displayed behind the notification content.
+ * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
+ * will work with any notification style.
+ *
+ * @return the background image
+ * @see android.app.Notification.WearableExtender#setBackground
+ */
+ public Bitmap getBackground() {
+ return mBackground;
+ }
+
+ /**
+ * Set an icon that goes with the content of this notification.
+ */
+ public WearableExtender setContentIcon(int icon) {
+ mContentIcon = icon;
+ return this;
+ }
+
+ /**
+ * Get an icon that goes with the content of this notification.
+ */
+ public int getContentIcon() {
+ return mContentIcon;
+ }
+
+ /**
+ * Set the gravity that the content icon should have within the notification display.
+ * Supported values include {@link android.view.Gravity#START} and
+ * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
+ * @see #setContentIcon
+ */
+ public WearableExtender setContentIconGravity(int contentIconGravity) {
+ mContentIconGravity = contentIconGravity;
+ return this;
+ }
+
+ /**
+ * Get the gravity that the content icon should have within the notification display.
+ * Supported values include {@link android.view.Gravity#START} and
+ * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
+ * @see #getContentIcon
+ */
+ public int getContentIconGravity() {
+ return mContentIconGravity;
+ }
+
+ /**
+ * Set an action from this notification's actions to be clickable with the content of
+ * this notification page. This action will no longer display separately from the
+ * notification content. This action's icon will display with optional subtext provided
+ * by the action's title.
+ * @param actionIndex The index of the action to hoist on the current notification page.
+ * If wearable actions are present, this index will apply to that list,
+ * otherwise it will apply to the main notification's actions list.
+ */
+ public WearableExtender setContentAction(int actionIndex) {
+ mContentActionIndex = actionIndex;
+ return this;
+ }
+
+ /**
+ * Get the action index of an action from this notification to show as clickable with
+ * the content of this notification page. When the user clicks this notification page,
+ * this action will trigger. This action will no longer display separately from the
+ * notification content. The action's icon will display with optional subtext provided
+ * by the action's title.
+ *
+ * <p>If wearable specific actions are present, this index will apply to that list,
+ * otherwise it will apply to the main notification's actions list.
+ */
+ public int getContentAction() {
+ return mContentActionIndex;
+ }
+
+ /**
+ * Set the gravity that this notification should have within the available viewport space.
+ * Supported values include {@link android.view.Gravity#TOP},
+ * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
+ * The default value is {@link android.view.Gravity#BOTTOM}.
+ */
+ public WearableExtender setGravity(int gravity) {
+ mGravity = gravity;
+ return this;
+ }
+
+ /**
+ * Get the gravity that this notification should have within the available viewport space.
+ * Supported values include {@link android.view.Gravity#TOP},
+ * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
+ * The default value is {@link android.view.Gravity#BOTTOM}.
+ */
+ public int getGravity() {
+ return mGravity;
+ }
+
+ /**
+ * Set the custom size preset for the display of this notification out of the available
+ * presets found in {@link android.app.Notification.WearableExtender}, e.g.
+ * {@link #SIZE_LARGE}.
+ * <p>Some custom size presets are only applicable for custom display notifications created
+ * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
+ * documentation for the preset in question. See also
+ * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
+ */
+ public WearableExtender setCustomSizePreset(int sizePreset) {
+ mCustomSizePreset = sizePreset;
+ return this;
+ }
+
+ /**
+ * Get the custom size preset for the display of this notification out of the available
+ * presets found in {@link android.app.Notification.WearableExtender}, e.g.
+ * {@link #SIZE_LARGE}.
+ * <p>Some custom size presets are only applicable for custom display notifications created
+ * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
+ * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
+ */
+ public int getCustomSizePreset() {
+ return mCustomSizePreset;
+ }
+
+ /**
+ * Set the custom height in pixels for the display of this notification's content.
+ * <p>This option is only available for custom display notifications created
+ * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
+ * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
+ * {@link #getCustomContentHeight}.
+ */
+ public WearableExtender setCustomContentHeight(int height) {
+ mCustomContentHeight = height;
+ return this;
+ }
+
+ /**
+ * Get the custom height in pixels for the display of this notification's content.
+ * <p>This option is only available for custom display notifications created
+ * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
+ * {@link #setCustomContentHeight}.
+ */
+ public int getCustomContentHeight() {
+ return mCustomContentHeight;
+ }
+
+ /**
+ * Set whether the scrolling position for the contents of this notification should start
+ * at the bottom of the contents instead of the top when the contents are too long to
+ * display within the screen. Default is false (start scroll at the top).
+ */
+ public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
+ setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
+ return this;
+ }
+
+ /**
+ * Get whether the scrolling position for the contents of this notification should start
+ * at the bottom of the contents instead of the top when the contents are too long to
+ * display within the screen. Default is false (start scroll at the top).
+ */
+ public boolean getStartScrollBottom() {
+ return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
+ }
+
+ /**
+ * Set whether the content intent is available when the wearable device is not connected
+ * to a companion device. The user can still trigger this intent when the wearable device
+ * is offline, but a visual hint will indicate that the content intent may not be available.
+ * Defaults to true.
+ */
+ public WearableExtender setContentIntentAvailableOffline(
+ boolean contentIntentAvailableOffline) {
+ setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
+ return this;
+ }
+
+ /**
+ * Get whether the content intent is available when the wearable device is not connected
+ * to a companion device. The user can still trigger this intent when the wearable device
+ * is offline, but a visual hint will indicate that the content intent may not be available.
+ * Defaults to true.
+ */
+ public boolean getContentIntentAvailableOffline() {
+ return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
+ }
+
+ /**
+ * Set a hint that this notification's icon should not be displayed.
+ * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
+ * @return this object for method chaining
+ */
+ public WearableExtender setHintHideIcon(boolean hintHideIcon) {
+ setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
+ return this;
+ }
+
+ /**
+ * Get a hint that this notification's icon should not be displayed.
+ * @return {@code true} if this icon should not be displayed, false otherwise.
+ * The default value is {@code false} if this was never set.
+ */
+ public boolean getHintHideIcon() {
+ return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
+ }
+
+ /**
+ * Set a visual hint that only the background image of this notification should be
+ * displayed, and other semantic content should be hidden. This hint is only applicable
+ * to sub-pages added using {@link #addPage}.
+ */
+ public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
+ setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
+ return this;
+ }
+
+ /**
+ * Get a visual hint that only the background image of this notification should be
+ * displayed, and other semantic content should be hidden. This hint is only applicable
+ * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
+ */
+ public boolean getHintShowBackgroundOnly() {
+ return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
+ }
+
+ private void setFlag(int mask, boolean value) {
+ if (value) {
+ mFlags |= mask;
+ } else {
+ mFlags &= ~mask;
+ }
+ }
+ }
+
+ /**
+ * Get an array of Notification objects from a parcelable array bundle field.
+ * Update the bundle to have a typed array so fetches in the future don't need
+ * to do an array copy.
+ */
+ private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
+ Parcelable[] array = bundle.getParcelableArray(key);
+ if (array instanceof Notification[] || array == null) {
+ return (Notification[]) array;
+ }
+ Notification[] typedArray = Arrays.copyOf(array, array.length,
+ Notification[].class);
+ bundle.putParcelableArray(key, typedArray);
+ return typedArray;
+ }
}
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
index 9cfc541..11420c5 100644
--- a/core/java/android/app/RemoteInput.java
+++ b/core/java/android/app/RemoteInput.java
@@ -64,18 +64,24 @@
/** Extra added to a clip data intent object to hold the results bundle. */
public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+ // Flags bitwise-ored to mFlags
+ private static final int FLAG_ALLOW_FREE_FORM_INPUT = 0x1;
+
+ // Default value for flags integer
+ private static final int DEFAULT_FLAGS = FLAG_ALLOW_FREE_FORM_INPUT;
+
private final String mResultKey;
private final CharSequence mLabel;
private final CharSequence[] mChoices;
- private final boolean mAllowFreeFormInput;
+ private final int mFlags;
private final Bundle mExtras;
private RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
- boolean allowFreeFormInput, Bundle extras) {
+ int flags, Bundle extras) {
this.mResultKey = resultKey;
this.mLabel = label;
this.mChoices = choices;
- this.mAllowFreeFormInput = allowFreeFormInput;
+ this.mFlags = flags;
this.mExtras = extras;
}
@@ -108,7 +114,7 @@
* if you set this to false and {@link #getChoices} returns {@code null} or empty.
*/
public boolean getAllowFreeFormInput() {
- return mAllowFreeFormInput;
+ return (mFlags & FLAG_ALLOW_FREE_FORM_INPUT) != 0;
}
/**
@@ -125,7 +131,7 @@
private final String mResultKey;
private CharSequence mLabel;
private CharSequence[] mChoices;
- private boolean mAllowFreeFormInput = true;
+ private int mFlags = DEFAULT_FLAGS;
private Bundle mExtras = new Bundle();
/**
@@ -178,7 +184,7 @@
* @return this object for method chaining
*/
public Builder setAllowFreeFormInput(boolean allowFreeFormInput) {
- mAllowFreeFormInput = allowFreeFormInput;
+ setFlag(mFlags, allowFreeFormInput);
return this;
}
@@ -205,12 +211,20 @@
return mExtras;
}
+ private void setFlag(int mask, boolean value) {
+ if (value) {
+ mFlags |= mask;
+ } else {
+ mFlags &= ~mask;
+ }
+ }
+
/**
* Combine all of the options that have been set and return a new {@link RemoteInput}
* object.
*/
public RemoteInput build() {
- return new RemoteInput(mResultKey, mLabel, mChoices, mAllowFreeFormInput, mExtras);
+ return new RemoteInput(mResultKey, mLabel, mChoices, mFlags, mExtras);
}
}
@@ -218,7 +232,7 @@
mResultKey = in.readString();
mLabel = in.readCharSequence();
mChoices = in.readCharSequenceArray();
- mAllowFreeFormInput = in.readInt() != 0;
+ mFlags = in.readInt();
mExtras = in.readBundle();
}
@@ -279,7 +293,7 @@
out.writeString(mResultKey);
out.writeCharSequence(mLabel);
out.writeCharSequenceArray(mChoices);
- out.writeInt(mAllowFreeFormInput ? 1 : 0);
+ out.writeInt(mFlags);
out.writeBundle(mExtras);
}
diff --git a/core/java/android/app/TaskManagerImpl.java b/core/java/android/app/TaskManagerImpl.java
new file mode 100644
index 0000000..f42839e
--- /dev/null
+++ b/core/java/android/app/TaskManagerImpl.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+// in android.app so ContextImpl has package access
+package android.app;
+
+import android.app.task.ITaskManager;
+import android.app.task.Task;
+import android.app.task.TaskManager;
+
+import java.util.List;
+
+
+/**
+ * Concrete implementation of the TaskManager interface
+ * @hide
+ */
+public class TaskManagerImpl extends TaskManager {
+ ITaskManager mBinder;
+
+ /* package */ TaskManagerImpl(ITaskManager binder) {
+ mBinder = binder;
+ }
+
+ @Override
+ public int schedule(Task task) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public void cancel(int taskId) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void cancelAll() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public List<Task> getAllPendingTasks() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 18e2a95..6b486c4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -32,6 +32,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.service.trust.TrustAgentService;
import android.util.Log;
@@ -175,15 +176,16 @@
public static final String ACTION_SET_NEW_PASSWORD
= "android.app.action.SET_NEW_PASSWORD";
/**
- * Flag for {@link #addForwardingIntentFilter}: the intents will forwarded to the primary user.
+ * Flag used by {@link #addCrossProfileIntentFilter} to allow access of certain intents from a
+ * managed profile to its parent.
*/
- public static int FLAG_TO_PRIMARY_USER = 0x0001;
+ public static int FLAG_PARENT_CAN_ACCESS_MANAGED = 0x0001;
/**
- * Flag for {@link #addForwardingIntentFilter}: the intents will be forwarded to the managed
- * profile.
+ * Flag used by {@link #addCrossProfileIntentFilter} to allow access of certain intents from the
+ * parent to its managed profile.
*/
- public static int FLAG_TO_MANAGED_PROFILE = 0x0002;
+ public static int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002;
/**
* Return true if the given administrator component is currently
@@ -1950,17 +1952,16 @@
}
/**
- * Called by a profile owner to forward intents sent from the managed profile to the owner, or
- * from the owner to the managed profile.
- * If an intent matches this intent filter, then activities belonging to the other user can
- * respond to this intent.
+ * Called by the profile owner so that some intents sent in the managed profile can also be
+ * resolved in the parent, or vice versa.
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param filter if an intent matches this IntentFilter, then it can be forwarded.
+ * @param filter The {@link IntentFilter} the intent has to match to be also resolved in the
+ * other profile
*/
- public void addForwardingIntentFilter(ComponentName admin, IntentFilter filter, int flags) {
+ public void addCrossProfileIntentFilter(ComponentName admin, IntentFilter filter, int flags) {
if (mService != null) {
try {
- mService.addForwardingIntentFilter(admin, filter, flags);
+ mService.addCrossProfileIntentFilter(admin, filter, flags);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1968,14 +1969,14 @@
}
/**
- * Called by a profile owner to remove the forwarding intent filters from the current user
- * and from the owner.
+ * Called by a profile owner to remove the cross-profile intent filters from the managed profile
+ * and from the parent.
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
*/
- public void clearForwardingIntentFilters(ComponentName admin) {
+ public void clearCrossProfileIntentFilters(ComponentName admin) {
if (mService != null) {
try {
- mService.clearForwardingIntentFilters(admin);
+ mService.clearCrossProfileIntentFilters(admin);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1983,6 +1984,43 @@
}
/**
+ * Called by a device owner to create a user with the specified name. The UserHandle returned
+ * by this method should not be persisted as user handles are recycled as users are removed and
+ * created. If you need to persist an identifier for this user, use
+ * {@link UserManager#getSerialNumberForUser}.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param name the user's name
+ * @see UserHandle
+ * @return the UserHandle object for the created user, or null if the user could not be created.
+ */
+ public UserHandle createUser(ComponentName admin, String name) {
+ try {
+ return mService.createUser(admin, name);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not create a user", re);
+ }
+ return null;
+ }
+
+ /**
+ * Called by a device owner to remove a user and all associated data. The primary user can
+ * not be removed.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param userHandle the user to remove.
+ * @return {@code true} if the user was removed, {@code false} otherwise.
+ */
+ public boolean removeUser(ComponentName admin, UserHandle userHandle) {
+ try {
+ return mService.removeUser(admin, userHandle);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not remove user ", re);
+ return false;
+ }
+ }
+
+ /**
* Called by a profile or device owner to get the application restrictions for a given target
* application running in the managed profile.
*
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 7257158..cc6d715 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -22,6 +22,7 @@
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.RemoteCallback;
+import android.os.UserHandle;
/**
* Internal IPC interface to the device policy service.
@@ -121,13 +122,16 @@
Bundle getApplicationRestrictions(in ComponentName who, in String packageName);
void setUserRestriction(in ComponentName who, in String key, boolean enable);
- void addForwardingIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
- void clearForwardingIntentFilters(in ComponentName admin);
+ void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
+ void clearCrossProfileIntentFilters(in ComponentName admin);
boolean setApplicationBlocked(in ComponentName admin, in String packageName, boolean blocked);
int setApplicationsBlocked(in ComponentName admin, in Intent intent, boolean blocked);
boolean isApplicationBlocked(in ComponentName admin, in String packageName);
+ UserHandle createUser(in ComponentName who, in String name);
+ boolean removeUser(in ComponentName who, in UserHandle userHandle);
+
void enableSystemApp(in ComponentName admin, in String packageName);
int enableSystemAppWithIntent(in ComponentName admin, in Intent intent);
diff --git a/core/java/android/app/task/ITaskManager.aidl b/core/java/android/app/task/ITaskManager.aidl
new file mode 100644
index 0000000..b56c78a
--- /dev/null
+++ b/core/java/android/app/task/ITaskManager.aidl
@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.task;
+
+import android.app.task.Task;
+
+ /**
+ * IPC interface that supports the app-facing {@link #TaskManager} api.
+ * {@hide}
+ */
+interface ITaskManager {
+ int schedule(in Task task);
+ void cancel(int taskId);
+ void cancelAll();
+ List<Task> getAllPendingTasks();
+}
diff --git a/core/java/android/app/task/Task.aidl b/core/java/android/app/task/Task.aidl
new file mode 100644
index 0000000..1f25439
--- /dev/null
+++ b/core/java/android/app/task/Task.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.task;
+
+parcelable Task;
+
\ No newline at end of file
diff --git a/core/java/android/app/task/Task.java b/core/java/android/app/task/Task.java
new file mode 100644
index 0000000..dd184a5
--- /dev/null
+++ b/core/java/android/app/task/Task.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.task;
+
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container of data passed to the {@link android.app.task.TaskManager} fully encapsulating the
+ * parameters required to schedule work against the calling application. These are constructed
+ * using the {@link Task.Builder}.
+ */
+public class Task implements Parcelable {
+
+ public interface NetworkType {
+ public final int ANY = 0;
+ public final int UNMETERED = 1;
+ }
+
+ /**
+ * Linear: retry_time(failure_time, t) = failure_time + initial_retry_delay * t, t >= 1
+ * Expon: retry_time(failure_time, t) = failure_time + initial_retry_delay ^ t, t >= 1
+ */
+ public interface BackoffPolicy {
+ public final int LINEAR = 0;
+ public final int EXPONENTIAL = 1;
+ }
+
+ private final int taskId;
+ // TODO: Change this to use PersistableBundle when that lands in master.
+ private final Bundle extras;
+ private final ComponentName service;
+ private final boolean requireCharging;
+ private final boolean requireDeviceIdle;
+ private final int networkCapabilities;
+ private final long minLatencyMillis;
+ private final long maxExecutionDelayMillis;
+ private final boolean isPeriodic;
+ private final long intervalMillis;
+ private final long initialBackoffMillis;
+ private final int backoffPolicy;
+
+ /**
+ * Unique task id associated with this class. This is assigned to your task by the scheduler.
+ */
+ public int getTaskId() {
+ return taskId;
+ }
+
+ /**
+ * Bundle of extras which are returned to your application at execution time.
+ */
+ public Bundle getExtras() {
+ return extras;
+ }
+
+ /**
+ * Name of the service endpoint that will be called back into by the TaskManager.
+ */
+ public ComponentName getService() {
+ return service;
+ }
+
+ /**
+ * Whether this task needs the device to be plugged in.
+ */
+ public boolean isRequireCharging() {
+ return requireCharging;
+ }
+
+ /**
+ * Whether this task needs the device to be in an Idle maintenance window.
+ */
+ public boolean isRequireDeviceIdle() {
+ return requireDeviceIdle;
+ }
+
+ /**
+ * See {@link android.app.task.Task.NetworkType} for a description of this value.
+ */
+ public int getNetworkCapabilities() {
+ return networkCapabilities;
+ }
+
+ /**
+ * Set for a task that does not recur periodically, to specify a delay after which the task
+ * will be eligible for execution. This value is not set if the task recurs periodically.
+ */
+ public long getMinLatencyMillis() {
+ return minLatencyMillis;
+ }
+
+ /**
+ * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the task recurs
+ * periodically.
+ */
+ public long getMaxExecutionDelayMillis() {
+ return maxExecutionDelayMillis;
+ }
+
+ /**
+ * Track whether this task will repeat with a given period.
+ */
+ public boolean isPeriodic() {
+ return isPeriodic;
+ }
+
+ /**
+ * Set to the interval between occurrences of this task. This value is <b>not</b> set if the
+ * task does not recur periodically.
+ */
+ public long getIntervalMillis() {
+ return intervalMillis;
+ }
+
+ /**
+ * The amount of time the TaskManager will wait before rescheduling a failed task. This value
+ * will be increased depending on the backoff policy specified at task creation time. Defaults
+ * to 5 seconds.
+ */
+ public long getInitialBackoffMillis() {
+ return initialBackoffMillis;
+ }
+
+ /**
+ * See {@link android.app.task.Task.BackoffPolicy} for an explanation of the values this field
+ * can take. This defaults to exponential.
+ */
+ public int getBackoffPolicy() {
+ return backoffPolicy;
+ }
+
+ private Task(Parcel in) {
+ taskId = in.readInt();
+ extras = in.readBundle();
+ service = ComponentName.readFromParcel(in);
+ requireCharging = in.readInt() == 1;
+ requireDeviceIdle = in.readInt() == 1;
+ networkCapabilities = in.readInt();
+ minLatencyMillis = in.readLong();
+ maxExecutionDelayMillis = in.readLong();
+ isPeriodic = in.readInt() == 1;
+ intervalMillis = in.readLong();
+ initialBackoffMillis = in.readLong();
+ backoffPolicy = in.readInt();
+ }
+
+ private Task(Task.Builder b) {
+ taskId = b.mTaskId;
+ extras = new Bundle(b.mExtras);
+ service = b.mTaskService;
+ requireCharging = b.mRequiresCharging;
+ requireDeviceIdle = b.mRequiresDeviceIdle;
+ networkCapabilities = b.mNetworkCapabilities;
+ minLatencyMillis = b.mMinLatencyMillis;
+ maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
+ isPeriodic = b.mIsPeriodic;
+ intervalMillis = b.mIntervalMillis;
+ initialBackoffMillis = b.mInitialBackoffMillis;
+ backoffPolicy = b.mBackoffPolicy;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(taskId);
+ out.writeBundle(extras);
+ ComponentName.writeToParcel(service, out);
+ out.writeInt(requireCharging ? 1 : 0);
+ out.writeInt(requireDeviceIdle ? 1 : 0);
+ out.writeInt(networkCapabilities);
+ out.writeLong(minLatencyMillis);
+ out.writeLong(maxExecutionDelayMillis);
+ out.writeInt(isPeriodic ? 1 : 0);
+ out.writeLong(intervalMillis);
+ out.writeLong(initialBackoffMillis);
+ out.writeInt(backoffPolicy);
+ }
+
+ public static final Creator<Task> CREATOR = new Creator<Task>() {
+ @Override
+ public Task createFromParcel(Parcel in) {
+ return new Task(in);
+ }
+
+ @Override
+ public Task[] newArray(int size) {
+ return new Task[size];
+ }
+ };
+
+ /**
+ * Builder class for constructing {@link Task} objects.
+ */
+ public final class Builder {
+ private int mTaskId;
+ private Bundle mExtras;
+ private ComponentName mTaskService;
+ // Requirements.
+ private boolean mRequiresCharging;
+ private boolean mRequiresDeviceIdle;
+ private int mNetworkCapabilities;
+ // One-off parameters.
+ private long mMinLatencyMillis;
+ private long mMaxExecutionDelayMillis;
+ // Periodic parameters.
+ private boolean mIsPeriodic;
+ private long mIntervalMillis;
+ // Back-off parameters.
+ private long mInitialBackoffMillis = 5000L;
+ private int mBackoffPolicy = BackoffPolicy.EXPONENTIAL;
+ /** Easy way to track whether the client has tried to set a back-off policy. */
+ private boolean mBackoffPolicySet = false;
+
+ /**
+ * @param taskId Application-provided id for this task. Subsequent calls to cancel, or
+ * tasks created with the same taskId, will update the pre-existing task with
+ * the same id.
+ * @param taskService The endpoint that you implement that will receive the callback from the
+ * TaskManager.
+ */
+ public Builder(int taskId, ComponentName taskService) {
+ mTaskService = taskService;
+ mTaskId = taskId;
+ }
+
+ /**
+ * Set optional extras. This is persisted, so we only allow primitive types.
+ * @param extras Bundle containing extras you want the scheduler to hold on to for you.
+ */
+ public Builder setExtras(Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Set some description of the kind of network capabilities you would like to have. This
+ * will be a parameter defined in {@link android.app.task.Task.NetworkType}.
+ * Not calling this function means the network is not necessary.
+ * Bear in mind that calling this function defines network as a strict requirement for your
+ * task if the network requested is not available your task will never run. See
+ * {@link #setOverrideDeadline(long)} to change this behaviour.
+ */
+ public Builder setRequiredNetworkCapabilities(int networkCapabilities) {
+ mNetworkCapabilities = networkCapabilities;
+ return this;
+ }
+
+ /*
+ * Specify that to run this task, the device needs to be plugged in. This defaults to
+ * false.
+ * @param requireCharging Whether or not the device is plugged in.
+ */
+ public Builder setRequiresCharging(boolean requiresCharging) {
+ mRequiresCharging = requiresCharging;
+ return this;
+ }
+
+ /**
+ * Specify that to run, the task needs the device to be in idle mode. This defaults to
+ * false.
+ * <p>Idle mode is a loose definition provided by the system, which means that the device
+ * is not in use, and has not been in use for some time. As such, it is a good time to
+ * perform resource heavy tasks. Bear in mind that battery usage will still be attributed
+ * to your application, and surfaced to the user in battery stats.</p>
+ * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance
+ * window.
+ */
+ public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
+ mRequiresDeviceIdle = requiresDeviceIdle;
+ return this;
+ }
+
+ /**
+ * Specify that this task should recur with the provided interval, not more than once per
+ * period. You have no control over when within this interval this task will be executed,
+ * only the guarantee that it will be executed at most once within this interval.
+ * A periodic task will be repeated until the phone is turned off, however it will only be
+ * persisted beyond boot if the client app has declared the
+ * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission. You can schedule
+ * periodic tasks without this permission, they simply will cease to exist after the phone
+ * restarts.
+ * Setting this function on the builder with {@link #setMinimumLatency(long)} or
+ * {@link #setOverrideDeadline(long)} will result in an error.
+ * @param intervalMillis Millisecond interval for which this task will repeat.
+ */
+ public Builder setPeriodic(long intervalMillis) {
+ mIsPeriodic = true;
+ mIntervalMillis = intervalMillis;
+ return this;
+ }
+
+ /**
+ * Specify that this task should be delayed by the provided amount of time.
+ * Because it doesn't make sense setting this property on a periodic task, doing so will
+ * throw an {@link java.lang.IllegalArgumentException} when
+ * {@link android.app.task.Task.Builder#build()} is called.
+ * @param minLatencyMillis Milliseconds before which this task will not be considered for
+ * execution.
+ */
+ public Builder setMinimumLatency(long minLatencyMillis) {
+ mMinLatencyMillis = minLatencyMillis;
+ return this;
+ }
+
+ /**
+ * Set deadline which is the maximum scheduling latency. The task will be run by this
+ * deadline even if other requirements are not met. Because it doesn't make sense setting
+ * this property on a periodic task, doing so will throw an
+ * {@link java.lang.IllegalArgumentException} when
+ * {@link android.app.task.Task.Builder#build()} is called.
+ */
+ public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
+ mMaxExecutionDelayMillis = maxExecutionDelayMillis;
+ return this;
+ }
+
+ /**
+ * Set up the back-off/retry policy.
+ * This defaults to some respectable values: {5 seconds, Exponential}. We cap back-off at
+ * 1hr.
+ * Note that trying to set a backoff criteria for a task with
+ * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build().
+ * This is because back-off typically does not make sense for these types of tasks. See
+ * {@link android.app.task.TaskService#taskFinished(android.app.task.TaskParams, boolean)}
+ * for more description of the return value for the case of a task executing while in idle
+ * mode.
+ * @param initialBackoffMillis Millisecond time interval to wait initially when task has
+ * failed.
+ * @param backoffPolicy is one of {@link BackoffPolicy}
+ */
+ public Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) {
+ mBackoffPolicySet = true;
+ mInitialBackoffMillis = initialBackoffMillis;
+ mBackoffPolicy = backoffPolicy;
+ return this;
+ }
+
+ /**
+ * @return The task object to hand to the TaskManager. This object is immutable.
+ */
+ public Task build() {
+ // Check that extras bundle only contains primitive types.
+ try {
+ for (String key : extras.keySet()) {
+ Object value = extras.get(key);
+ if (value == null) continue;
+ if (value instanceof Long) continue;
+ if (value instanceof Integer) continue;
+ if (value instanceof Boolean) continue;
+ if (value instanceof Float) continue;
+ if (value instanceof Double) continue;
+ if (value instanceof String) continue;
+ throw new IllegalArgumentException("Unexpected value type: "
+ + value.getClass().getName());
+ }
+ } catch (IllegalArgumentException e) {
+ throw e;
+ } catch (RuntimeException exc) {
+ throw new IllegalArgumentException("error unparcelling Bundle", exc);
+ }
+ // Check that a deadline was not set on a periodic task.
+ if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) {
+ throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
+ "periodic task.");
+ }
+ if (mIsPeriodic && (mMinLatencyMillis != 0L)) {
+ throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
+ "periodic task");
+ }
+ if (mBackoffPolicySet && mRequiresDeviceIdle) {
+ throw new IllegalArgumentException("An idle mode task will not respect any" +
+ " back-off policy, so calling setBackoffCriteria with" +
+ " setRequiresDeviceIdle is an error.");
+ }
+ return new Task(this);
+ }
+ }
+
+}
diff --git a/core/java/android/app/task/TaskManager.java b/core/java/android/app/task/TaskManager.java
new file mode 100644
index 0000000..0fbe37d
--- /dev/null
+++ b/core/java/android/app/task/TaskManager.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.task;
+
+import java.util.List;
+
+import android.content.Context;
+
+/**
+ * Class for scheduling various types of tasks with the scheduling framework on the device.
+ *
+ * <p>You do not
+ * instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.TASK_SERVICE)}.
+ */
+public abstract class TaskManager {
+ /*
+ * Returned from {@link #schedule(Task)} when an invalid parameter was supplied. This can occur
+ * if the run-time for your task is too short, or perhaps the system can't resolve the
+ * requisite {@link TaskService} in your package.
+ */
+ public static final int RESULT_INVALID_PARAMETERS = -1;
+
+ /**
+ * Returned from {@link #schedule(Task)} if this application has made too many requests for
+ * work over too short a time.
+ */
+ // TODO: Determine if this is necessary.
+ public static final int RESULT_OVER_QUOTA = -2;
+
+ /**
+ * @param task The task you wish scheduled. See
+ * {@link android.app.task.Task.Builder Task.Builder} for more detail on the sorts of tasks
+ * you can schedule.
+ * @return If >0, this int returns the taskId of the successfully scheduled task.
+ * Otherwise you have to compare the return value to the error codes defined in this class.
+ */
+ public abstract int schedule(Task task);
+
+ /**
+ * Cancel a task that is pending in the TaskManager.
+ * @param taskId unique identifier for this task. Obtain this value from the tasks returned by
+ * {@link #getAllPendingTasks()}.
+ * @return
+ */
+ public abstract void cancel(int taskId);
+
+ /**
+ * Cancel all tasks that have been registered with the TaskManager by this package.
+ */
+ public abstract void cancelAll();
+
+ /**
+ * @return a list of all the tasks registered by this package that have not yet been executed.
+ */
+ public abstract List<Task> getAllPendingTasks();
+
+}
diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/task/TaskParams.java
index 0351082..dacb3480 100644
--- a/core/java/android/app/task/TaskParams.java
+++ b/core/java/android/app/task/TaskParams.java
@@ -47,7 +47,7 @@
/**
* @return The extras you passed in when constructing this task with
- * {@link android.content.Task.Builder#setExtras(android.os.Bundle)}. This will
+ * {@link android.app.task.Task.Builder#setExtras(android.os.Bundle)}. This will
* never be null. If you did not set any extras this will be an empty bundle.
*/
public Bundle getExtras() {
diff --git a/core/java/android/app/task/TaskService.java b/core/java/android/app/task/TaskService.java
index ab1a565..8ce4484 100644
--- a/core/java/android/app/task/TaskService.java
+++ b/core/java/android/app/task/TaskService.java
@@ -28,7 +28,7 @@
import com.android.internal.annotations.GuardedBy;
/**
- * <p>Entry point for the callback from the {@link android.content.TaskManager}.</p>
+ * <p>Entry point for the callback from the {@link android.app.task.TaskManager}.</p>
* <p>This is the base class that handles asynchronous requests that were previously scheduled. You
* are responsible for overriding {@link TaskService#onStartTask(TaskParams)}, which is where
* you will implement your task logic.</p>
@@ -215,9 +215,9 @@
*
* <p>This will happen if the requirements specified at schedule time are no longer met. For
* example you may have requested WiFi with
- * {@link android.content.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your
+ * {@link android.app.task.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your
* task was executing the user toggled WiFi. Another example is if you had specified
- * {@link android.content.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
+ * {@link android.app.task.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
* idle maintenance window. You are solely responsible for the behaviour of your application
* upon receipt of this message; your app will likely start to misbehave if you ignore it. One
* immediate repercussion is that the system will cease holding a wakelock for you.</p>
@@ -237,7 +237,7 @@
* You can specify post-execution behaviour to the scheduler here with
* <code>needsReschedule </code>. This will apply a back-off timer to your task based on
* the default, or what was set with
- * {@link android.content.Task.Builder#setBackoffCriteria(long, int)}. The original
+ * {@link android.app.task.Task.Builder#setBackoffCriteria(long, int)}. The original
* requirements are always honoured even for a backed-off task. Note that a task running in
* idle mode will not be backed-off. Instead what will happen is the task will be re-added
* to the queue and re-executed within a future idle maintenance window.
diff --git a/core/java/android/app/wearable/WearableActionExtensions.java b/core/java/android/app/wearable/WearableActionExtensions.java
deleted file mode 100644
index c296ef2..0000000
--- a/core/java/android/app/wearable/WearableActionExtensions.java
+++ /dev/null
@@ -1,176 +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.app.wearable;
-
-import android.app.Notification;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Wearable extensions to notification actions. To add extensions to an action,
- * create a new {@link WearableActionExtensions} object using
- * {@link WearableActionExtensions.Builder} and apply it to a
- * {@link android.app.Notification.Action.Builder}.
- *
- * <pre class="prettyprint">
- * Notification.Action action = new Notification.Action.Builder(
- * R.drawable.archive_all, "Archive all", actionIntent)
- * .apply(new WearableActionExtensions.Builder()
- * .setAvailableOffline(false)
- * .build())
- * .build();
- * </pre>
- */
-public final class WearableActionExtensions implements Notification.Action.Builder.Extender,
- Parcelable {
- /** Notification action extra which contains wearable extensions */
- private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
-
- // Flags bitwise-ored to mFlags
- private static final int FLAG_AVAILABLE_OFFLINE = 1 << 0;
-
- // Default value for flags integer
- private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
-
- private final int mFlags;
-
- private WearableActionExtensions(int flags) {
- mFlags = flags;
- }
-
- private WearableActionExtensions(Parcel in) {
- mFlags = in.readInt();
- }
-
- /**
- * Create a {@link WearableActionExtensions} by reading wearable extensions present on an
- * existing notification action.
- * @param action the notification action to inspect.
- * @return a new {@link WearableActionExtensions} object.
- */
- public static WearableActionExtensions from(Notification.Action action) {
- WearableActionExtensions extensions = action.getExtras().getParcelable(
- EXTRA_WEARABLE_EXTENSIONS);
- if (extensions != null) {
- return extensions;
- } else {
- // Return a WearableActionExtensions with default values.
- return new Builder().build();
- }
- }
-
- /**
- * Get whether this action is available when the wearable device is not connected to
- * a companion device. The user can still trigger this action when the wearable device is
- * offline, but a visual hint will indicate that the action may not be available.
- * Defaults to true.
- */
- public boolean isAvailableOffline() {
- return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
- }
-
- @Override
- public Notification.Action.Builder applyTo(Notification.Action.Builder builder) {
- builder.getExtras().putParcelable(EXTRA_WEARABLE_EXTENSIONS, this);
- return builder;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(mFlags);
- }
-
- /**
- * Builder for {@link WearableActionExtensions} objects, which adds wearable extensions to
- * notification actions. To extend an action, create an instance of this class, call the set
- * methods present, call {@link #build}, and finally apply the options to a
- * {@link Notification.Action.Builder} using its
- * {@link android.app.Notification.Action.Builder#apply} method.
- */
- public static final class Builder {
- private int mFlags = DEFAULT_FLAGS;
-
- /**
- * Construct a builder to be used for adding wearable extensions to notification actions.
- *
- * <pre class="prettyprint">
- * Notification.Action action = new Notification.Action.Builder(
- * R.drawable.archive_all, "Archive all", actionIntent)
- * .apply(new WearableActionExtensions.Builder()
- * .setAvailableOffline(false)
- * .build())
- * .build();</pre>
- */
- public Builder() {
- }
-
- /**
- * Create a {@link Builder} by reading wearable extensions present on an
- * existing {@code WearableActionExtensions} object.
- * @param other the existing extensions to inspect.
- */
- public Builder(WearableActionExtensions other) {
- mFlags = other.mFlags;
- }
-
- /**
- * Set whether this action is available when the wearable device is not connected to
- * a companion device. The user can still trigger this action when the wearable device is
- * offline, but a visual hint will indicate that the action may not be available.
- * Defaults to true.
- */
- public Builder setAvailableOffline(boolean availableOffline) {
- setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
- return this;
- }
-
- /**
- * Build a new {@link WearableActionExtensions} object with the extensions
- * currently present on this builder.
- * @return the extensions object.
- */
- public WearableActionExtensions build() {
- return new WearableActionExtensions(mFlags);
- }
-
- private void setFlag(int mask, boolean value) {
- if (value) {
- mFlags |= mask;
- } else {
- mFlags &= ~mask;
- }
- }
- }
-
- public static final Creator<WearableActionExtensions> CREATOR =
- new Creator<WearableActionExtensions>() {
- @Override
- public WearableActionExtensions createFromParcel(Parcel in) {
- return new WearableActionExtensions(in);
- }
-
- @Override
- public WearableActionExtensions[] newArray(int size) {
- return new WearableActionExtensions[size];
- }
- };
-}
diff --git a/core/java/android/app/wearable/WearableNotificationExtensions.java b/core/java/android/app/wearable/WearableNotificationExtensions.java
deleted file mode 100644
index d433613..0000000
--- a/core/java/android/app/wearable/WearableNotificationExtensions.java
+++ /dev/null
@@ -1,702 +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.app.wearable;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.graphics.Bitmap;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.Gravity;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Helper class that contains wearable extensions for notifications.
- * <p class="note"> See
- * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
- * for Android Wear</a> for more information on how to use this class.
- * <p>
- * To create a notification with wearable extensions:
- * <ol>
- * <li>Create a {@link Notification.Builder}, setting any desired
- * properties.
- * <li>Create a {@link WearableNotificationExtensions.Builder}.
- * <li>Set wearable-specific properties using the
- * {@code add} and {@code set} methods of {@link WearableNotificationExtensions.Builder}.
- * <li>Call {@link WearableNotificationExtensions.Builder#build} to build the extensions
- * object.
- * <li>Call {@link Notification.Builder#apply} to apply the extensions to a notification.
- * <li>Post the notification to the notification system with the
- * {@code NotificationManager.notify(...)} methods.
- * </ol>
- *
- * <pre class="prettyprint">
- * Notification notif = new Notification.Builder(mContext)
- * .setContentTitle("New mail from " + sender.toString())
- * .setContentText(subject)
- * .setSmallIcon(R.drawable.new_mail)
- * .apply(new new WearableNotificationExtensions.Builder()
- * .setContentIcon(R.drawable.new_mail)
- * .build())
- * .build();
- * NotificationManager notificationManger =
- * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- * notificationManger.notify(0, notif);</pre>
- *
- * <p>Wearable extensions can be accessed on an existing notification by using the
- * {@link WearableNotificationExtensions#from} function.
- *
- * <pre class="prettyprint">
- * WearableNotificationExtensions wearableExtensions = WearableNotificationExtensions.from(
- * notification);
- * Notification[] pages = wearableExtensions.getPages();
- * </pre>
- */
-public final class WearableNotificationExtensions implements Notification.Builder.Extender,
- Parcelable {
- /**
- * Sentinel value for an action index that is unset.
- */
- public static final int UNSET_ACTION_INDEX = -1;
-
- /**
- * Size value for use with {@link Builder#setCustomSizePreset} to show this notification with
- * default sizing.
- * <p>For custom display notifications created using {@link Builder#setDisplayIntent},
- * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
- * on their content.
- */
- public static final int SIZE_DEFAULT = 0;
-
- /**
- * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
- * with an extra small size.
- * <p>This value is only applicable for custom display notifications created using
- * {@link Builder#setDisplayIntent}.
- */
- public static final int SIZE_XSMALL = 1;
-
- /**
- * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
- * with a small size.
- * <p>This value is only applicable for custom display notifications created using
- * {@link Builder#setDisplayIntent}.
- */
- public static final int SIZE_SMALL = 2;
-
- /**
- * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
- * with a medium size.
- * <p>This value is only applicable for custom display notifications created using
- * {@link Builder#setDisplayIntent}.
- */
- public static final int SIZE_MEDIUM = 3;
-
- /**
- * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
- * with a large size.
- * <p>This value is only applicable for custom display notifications created using
- * {@link Builder#setDisplayIntent}.
- */
- public static final int SIZE_LARGE = 4;
-
- /** Notification extra which contains wearable extensions */
- static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
-
- // Flags bitwise-ored to mFlags
- static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 1 << 0;
- static final int FLAG_HINT_HIDE_ICON = 1 << 1;
- static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
- static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
-
- // Default value for flags integer
- static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
-
- private final Notification.Action[] mActions;
- private final int mFlags;
- private final PendingIntent mDisplayIntent;
- private final Notification[] mPages;
- private final Bitmap mBackground;
- private final int mContentIcon;
- private final int mContentIconGravity;
- private final int mContentActionIndex;
- private final int mCustomSizePreset;
- private final int mCustomContentHeight;
- private final int mGravity;
-
- private WearableNotificationExtensions(Notification.Action[] actions, int flags,
- PendingIntent displayIntent, Notification[] pages, Bitmap background,
- int contentIcon, int contentIconGravity, int contentActionIndex,
- int customSizePreset, int customContentHeight, int gravity) {
- mActions = actions;
- mFlags = flags;
- mDisplayIntent = displayIntent;
- mPages = pages;
- mBackground = background;
- mContentIcon = contentIcon;
- mContentIconGravity = contentIconGravity;
- mContentActionIndex = contentActionIndex;
- mCustomSizePreset = customSizePreset;
- mCustomContentHeight = customContentHeight;
- mGravity = gravity;
- }
-
- private WearableNotificationExtensions(Parcel in) {
- mActions = in.createTypedArray(Notification.Action.CREATOR);
- mFlags = in.readInt();
- mDisplayIntent = in.readParcelable(PendingIntent.class.getClassLoader());
- mPages = in.createTypedArray(Notification.CREATOR);
- mBackground = in.readParcelable(Bitmap.class.getClassLoader());
- mContentIcon = in.readInt();
- mContentIconGravity = in.readInt();
- mContentActionIndex = in.readInt();
- mCustomSizePreset = in.readInt();
- mCustomContentHeight = in.readInt();
- mGravity = in.readInt();
- }
-
- /**
- * Create a {@link WearableNotificationExtensions} by reading wearable extensions present on an
- * existing notification.
- * @param notif the notification to inspect.
- * @return a new {@link WearableNotificationExtensions} object.
- */
- public static WearableNotificationExtensions from(Notification notif) {
- WearableNotificationExtensions extensions = notif.extras.getParcelable(
- EXTRA_WEARABLE_EXTENSIONS);
- if (extensions != null) {
- return extensions;
- } else {
- // Return a WearableNotificationExtensions with default values.
- return new Builder().build();
- }
- }
-
- /**
- * Apply wearable extensions to a notification that is being built. This is typically
- * called by {@link Notification.Builder#apply} method of {@link Notification.Builder}.
- */
- @Override
- public Notification.Builder applyTo(Notification.Builder builder) {
- builder.getExtras().putParcelable(EXTRA_WEARABLE_EXTENSIONS, this);
- return builder;
- }
-
- /**
- * Get the number of wearable actions present on this notification.
- *
- * @return the number of wearable actions for this notification
- */
- public int getActionCount() {
- return mActions.length;
- }
-
- /**
- * Get a {@link Notification.Action} for the wearable action at {@code actionIndex}.
- * @param actionIndex the index of the desired wearable action
- */
- public Notification.Action getAction(int actionIndex) {
- return mActions[actionIndex];
- }
-
- /**
- * Get the wearable actions present on this notification.
- */
- public Notification.Action[] getActions() {
- return mActions;
- }
-
- /**
- * Get the intent to launch inside of an activity view when displaying this
- * notification. This {@code PendingIntent} should be for an activity.
- */
- public PendingIntent getDisplayIntent() {
- return mDisplayIntent;
- }
-
- /**
- * Get the array of additional pages of content for displaying this notification. The
- * current notification forms the first page, and elements within this array form
- * subsequent pages. This field can be used to separate a notification into multiple
- * sections.
- * @return the pages for this notification
- */
- public Notification[] getPages() {
- return mPages;
- }
-
- /**
- * Get a background image to be displayed behind the notification content.
- * Contrary to the {@link Notification.BigPictureStyle}, this background
- * will work with any notification style.
- *
- * @return the background image
- * @see Builder#setBackground
- */
- public Bitmap getBackground() {
- return mBackground;
- }
-
- /**
- * Get an icon that goes with the content of this notification.
- */
- public int getContentIcon() {
- return mContentIcon;
- }
-
- /**
- * Get the gravity that the content icon should have within the notification display.
- * Supported values include {@link Gravity#START} and {@link Gravity#END}. The default
- * value is {@link android.view.Gravity#END}.
- * @see #getContentIcon
- */
- public int getContentIconGravity() {
- return mContentIconGravity;
- }
-
- /**
- * Get the action index of an action from this notification to show as clickable with
- * the content of this notification page. When the user clicks this notification page,
- * this action will trigger. This action will no longer display separately from the
- * notification content. The action's icon will display with optional subtext provided
- * by the action's title.
- *
- * <p>If wearable specific actions are present, this index will apply to that list,
- * otherwise it will apply to the main notification's actions list.
- */
- public int getContentAction() {
- return mContentActionIndex;
- }
-
- /**
- * Get the gravity that this notification should have within the available viewport space.
- * Supported values include {@link Gravity#TOP}, {@link Gravity#CENTER_VERTICAL} and
- * {@link android.view.Gravity#BOTTOM}. The default value is
- * {@link android.view.Gravity#BOTTOM}.
- */
- public int getGravity() {
- return mGravity;
- }
-
- /**
- * Get the custom size preset for the display of this notification out of the available
- * presets found in {@link WearableNotificationExtensions}, e.g. {@link #SIZE_LARGE}.
- * <p>Some custom size presets are only applicable for custom display notifications created
- * using {@link Builder#setDisplayIntent}. Check the documentation for the preset in question.
- * See also {@link Builder#setCustomContentHeight} and {@link Builder#setCustomSizePreset}.
- */
- public int getCustomSizePreset() {
- return mCustomSizePreset;
- }
-
- /**
- * Get the custom height in pixels for the display of this notification's content.
- * <p>This option is only available for custom display notifications created
- * using {@link Builder#setDisplayIntent}. See also {@link Builder#setCustomSizePreset} and
- * {@link Builder#setCustomContentHeight}.
- */
- public int getCustomContentHeight() {
- return mCustomContentHeight;
- }
-
- /**
- * Get whether the scrolling position for the contents of this notification should start
- * at the bottom of the contents instead of the top when the contents are too long to
- * display within the screen. Default is false (start scroll at the top).
- */
- public boolean getStartScrollBottom() {
- return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
- }
-
- /**
- * Get whether the content intent is available when the wearable device is not connected
- * to a companion device. The user can still trigger this intent when the wearable device is
- * offline, but a visual hint will indicate that the content intent may not be available.
- * Defaults to true.
- */
- public boolean getContentIntentAvailableOffline() {
- return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
- }
-
- /**
- * Get a hint that this notification's icon should not be displayed.
- * @return {@code true} if this icon should not be displayed, false otherwise.
- * The default value is {@code false} if this was never set.
- */
- public boolean getHintHideIcon() {
- return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
- }
-
- /**
- * Get a visual hint that only the background image of this notification should be
- * displayed, and other semantic content should be hidden. This hint is only applicable
- * to sub-pages added using {@link Builder#addPage}.
- */
- public boolean getHintShowBackgroundOnly() {
- return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeTypedArray(mActions, flags);
- out.writeInt(mFlags);
- out.writeParcelable(mDisplayIntent, flags);
- out.writeTypedArray(mPages, flags);
- out.writeParcelable(mBackground, flags);
- out.writeInt(mContentIcon);
- out.writeInt(mContentIconGravity);
- out.writeInt(mContentActionIndex);
- out.writeInt(mCustomSizePreset);
- out.writeInt(mCustomContentHeight);
- out.writeInt(mGravity);
- }
-
- /**
- * Builder to apply wearable notification extensions to a {@link Notification.Builder}
- * object.
- *
- * <p>You can chain the "set" methods for this builder in any order,
- * but you must call the {@link #build} method and then the {@link Notification.Builder#apply}
- * method to apply your extensions to a notification.
- *
- * <pre class="prettyprint">
- * Notification notif = new Notification.Builder(mContext)
- * .setContentTitle("New mail from " + sender.toString())
- * .setContentText(subject)
- * .setSmallIcon(R.drawable.new_mail);
- * .apply(new WearableNotificationExtensions.Builder()
- * .setContentIcon(R.drawable.new_mail)
- * .build())
- * .build();
- * NotificationManager notificationManger =
- * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- * notificationManager.notify(0, notif);</pre>
- */
- public static final class Builder {
- private final List<Notification.Action> mActions =
- new ArrayList<Notification.Action>();
- private int mFlags = DEFAULT_FLAGS;
- private PendingIntent mDisplayIntent;
- private final List<Notification> mPages = new ArrayList<Notification>();
- private Bitmap mBackground;
- private int mContentIcon;
- private int mContentIconGravity = Gravity.END;
- private int mContentActionIndex = UNSET_ACTION_INDEX;
- private int mCustomContentHeight;
- private int mCustomSizePreset = SIZE_DEFAULT;
- private int mGravity = Gravity.BOTTOM;
-
- /**
- * Construct a builder to be used for adding wearable extensions to notifications.
- *
- * <pre class="prettyprint">
- * Notification notif = new Notification.Builder(mContext)
- * .setContentTitle("New mail from " + sender.toString())
- * .setContentText(subject)
- * .setSmallIcon(R.drawable.new_mail);
- * .apply(new WearableNotificationExtensions.Builder()
- * .setContentIcon(R.drawable.new_mail)
- * .build())
- * .build();
- * NotificationManager notificationManger =
- * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- * notificationManager.notify(0, notif);</pre>
- */
- public Builder() {
- }
-
- /**
- * Create a {@link Builder} by reading wearable extensions present on an
- * existing {@code WearableNotificationExtensions} object.
- * @param other the existing extensions to inspect.
- */
- public Builder(WearableNotificationExtensions other) {
- Collections.addAll(mActions, other.mActions);
- mFlags = other.mFlags;
- mDisplayIntent = other.mDisplayIntent;
- Collections.addAll(mPages, other.mPages);
- mBackground = other.mBackground;
- mContentIcon = other.mContentIcon;
- mContentIconGravity = other.mContentIconGravity;
- mContentActionIndex = other.mContentActionIndex;
- mCustomContentHeight = other.mCustomContentHeight;
- mCustomSizePreset = other.mCustomSizePreset;
- mGravity = other.mGravity;
- }
-
- /**
- * Add a wearable action to this notification.
- *
- * <p>When wearable actions are added using this method, the set of actions that
- * show on a wearable device splits from devices that only show actions added
- * using {@link android.app.Notification.Builder#addAction}. This allows for customization
- * of which actions display on different devices.
- *
- * @param action the action to add to this notification
- * @return this object for method chaining
- * @see Notification.Action
- */
- public Builder addAction(Notification.Action action) {
- mActions.add(action);
- return this;
- }
-
- /**
- * Adds wearable actions to this notification.
- *
- * <p>When wearable actions are added using this method, the set of actions that
- * show on a wearable device splits from devices that only show actions added
- * using {@link android.app.Notification.Builder#addAction}. This allows for customization
- * of which actions display on different devices.
- *
- * @param actions the actions to add to this notification
- * @return this object for method chaining
- * @see Notification.Action
- */
- public Builder addActions(List<Notification.Action> actions) {
- mActions.addAll(actions);
- return this;
- }
-
- /**
- * Clear all wearable actions present on this builder.
- * @return this object for method chaining.
- * @see #addAction
- */
- public Builder clearActions() {
- mActions.clear();
- return this;
- }
-
- /**
- * Set an intent to launch inside of an activity view when displaying
- * this notification. This {@link android.app.PendingIntent} should be for an activity.
- *
- * @param intent the {@link android.app.PendingIntent} for an activity
- * @return this object for method chaining
- * @see WearableNotificationExtensions#getDisplayIntent
- */
- public Builder setDisplayIntent(PendingIntent intent) {
- mDisplayIntent = intent;
- return this;
- }
-
- /**
- * Add an additional page of content to display with this notification. The current
- * notification forms the first page, and pages added using this function form
- * subsequent pages. This field can be used to separate a notification into multiple
- * sections.
- *
- * @param page the notification to add as another page
- * @return this object for method chaining
- * @see WearableNotificationExtensions#getPages
- */
- public Builder addPage(Notification page) {
- mPages.add(page);
- return this;
- }
-
- /**
- * Add additional pages of content to display with this notification. The current
- * notification forms the first page, and pages added using this function form
- * subsequent pages. This field can be used to separate a notification into multiple
- * sections.
- *
- * @param pages a list of notifications
- * @return this object for method chaining
- * @see WearableNotificationExtensions#getPages
- */
- public Builder addPages(List<Notification> pages) {
- mPages.addAll(pages);
- return this;
- }
-
- /**
- * Clear all additional pages present on this builder.
- * @return this object for method chaining.
- * @see #addPage
- */
- public Builder clearPages() {
- mPages.clear();
- return this;
- }
-
- /**
- * Set a background image to be displayed behind the notification content.
- * Contrary to the {@link Notification.BigPictureStyle}, this background
- * will work with any notification style.
- *
- * @param background the background bitmap
- * @return this object for method chaining
- * @see WearableNotificationExtensions#getBackground
- */
- public Builder setBackground(Bitmap background) {
- mBackground = background;
- return this;
- }
-
- /**
- * Set an icon that goes with the content of this notification.
- */
- public Builder setContentIcon(int icon) {
- mContentIcon = icon;
- return this;
- }
-
- /**
- * Set the gravity that the content icon should have within the notification display.
- * Supported values include {@link Gravity#START} and {@link Gravity#END}. The default
- * value is {@link android.view.Gravity#END}.
- * @see #setContentIcon
- */
- public Builder setContentIconGravity(int contentIconGravity) {
- mContentIconGravity = contentIconGravity;
- return this;
- }
-
- /**
- * Set an action from this notification's actions to be clickable with the content of
- * this notification page. This action will no longer display separately from the
- * notification content. This action's icon will display with optional subtext provided
- * by the action's title.
- * @param actionIndex The index of the action to hoist on the current notification page.
- * If wearable actions are present, this index will apply to that list,
- * otherwise it will apply to the main notification's actions list.
- */
- public Builder setContentAction(int actionIndex) {
- mContentActionIndex = actionIndex;
- return this;
- }
-
- /**
- * Set the gravity that this notification should have within the available viewport space.
- * Supported values include {@link Gravity#TOP}, {@link Gravity#CENTER_VERTICAL} and
- * {@link Gravity#BOTTOM}. The default value is {@link Gravity#BOTTOM}.
- */
- public Builder setGravity(int gravity) {
- mGravity = gravity;
- return this;
- }
-
- /**
- * Set the custom size preset for the display of this notification out of the available
- * presets found in {@link WearableNotificationExtensions}, e.g. {@link #SIZE_LARGE}.
- * <p>Some custom size presets are only applicable for custom display notifications created
- * using {@link Builder#setDisplayIntent}. Check the documentation for the preset in
- * question. See also {@link Builder#setCustomContentHeight} and
- * {@link #getCustomSizePreset}.
- */
- public Builder setCustomSizePreset(int sizePreset) {
- mCustomSizePreset = sizePreset;
- return this;
- }
-
- /**
- * Set the custom height in pixels for the display of this notification's content.
- * <p>This option is only available for custom display notifications created
- * using {@link Builder#setDisplayIntent}. See also {@link Builder#setCustomSizePreset} and
- * {@link #getCustomContentHeight}.
- */
- public Builder setCustomContentHeight(int height) {
- mCustomContentHeight = height;
- return this;
- }
-
- /**
- * Set whether the scrolling position for the contents of this notification should start
- * at the bottom of the contents instead of the top when the contents are too long to
- * display within the screen. Default is false (start scroll at the top).
- */
- public Builder setStartScrollBottom(boolean startScrollBottom) {
- setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
- return this;
- }
-
- /**
- * Set whether the content intent is available when the wearable device is not connected
- * to a companion device. The user can still trigger this intent when the wearable device
- * is offline, but a visual hint will indicate that the content intent may not be available.
- * Defaults to true.
- */
- public Builder setContentIntentAvailableOffline(boolean contentIntentAvailableOffline) {
- setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
- return this;
- }
-
- /**
- * Set a hint that this notification's icon should not be displayed.
- * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
- * @return this object for method chaining
- */
- public Builder setHintHideIcon(boolean hintHideIcon) {
- setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
- return this;
- }
-
- /**
- * Set a visual hint that only the background image of this notification should be
- * displayed, and other semantic content should be hidden. This hint is only applicable
- * to sub-pages added using {@link #addPage}.
- */
- public Builder setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
- setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
- return this;
- }
-
- /**
- * Build a new {@link WearableNotificationExtensions} object with the extensions
- * currently present on this builder.
- * @return the extensions object.
- */
- public WearableNotificationExtensions build() {
- return new WearableNotificationExtensions(
- mActions.toArray(new Notification.Action[mActions.size()]), mFlags,
- mDisplayIntent, mPages.toArray(new Notification[mPages.size()]),
- mBackground, mContentIcon, mContentIconGravity, mContentActionIndex,
- mCustomSizePreset, mCustomContentHeight, mGravity);
- }
-
- private void setFlag(int mask, boolean value) {
- if (value) {
- mFlags |= mask;
- } else {
- mFlags &= ~mask;
- }
- }
- }
-
- public static final Creator<WearableNotificationExtensions> CREATOR =
- new Creator<WearableNotificationExtensions>() {
- @Override
- public WearableNotificationExtensions createFromParcel(Parcel in) {
- return new WearableNotificationExtensions(in);
- }
-
- @Override
- public WearableNotificationExtensions[] newArray(int size) {
- return new WearableNotificationExtensions[size];
- }
- };
-}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index d0ac9c9..b0673b5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1994,6 +1994,7 @@
WIFI_PASSPOINT_SERVICE,
WIFI_P2P_SERVICE,
WIFI_SCANNING_SERVICE,
+ //@hide: ETHERNET_SERVICE,
NSD_SERVICE,
AUDIO_SERVICE,
MEDIA_ROUTER_SERVICE,
@@ -2022,6 +2023,7 @@
PRINT_SERVICE,
MEDIA_SESSION_SERVICE,
BATTERY_SERVICE,
+ TASK_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -2069,9 +2071,6 @@
* <dt> {@link #WIFI_P2P_SERVICE} ("wifip2p")
* <dd> A {@link android.net.wifi.p2p.WifiP2pManager WifiP2pManager} for management of
* Wi-Fi Direct connectivity.
- * <dt> {@link #ETHERNET_SERVICE} ("ethernet")
- * <dd> A {@link android.net.ethernet.EthernetManager EthernetManager} for
- * management of Ethernet connectivity.
* <dt> {@link #INPUT_METHOD_SERVICE} ("input_method")
* <dd> An {@link android.view.inputmethod.InputMethodManager InputMethodManager}
* for management of input methods.
@@ -2081,6 +2080,8 @@
* <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads
* <dt> {@link #BATTERY_SERVICE} ("batterymanager")
* <dd> A {@link android.os.BatteryManager} for managing battery state
+ * <dt> {@link #TASK_SERVICE} ("taskmanager")
+ * <dd> A {@link android.app.task.TaskManager} for managing scheduled tasks
* </dl>
*
* <p>Note: System services obtained via this API may be closely associated with
@@ -2136,6 +2137,8 @@
* @see android.app.DownloadManager
* @see #BATTERY_SERVICE
* @see android.os.BatteryManager
+ * @see #TASK_SERVICE
+ * @see android.app.task.TaskManager
*/
public abstract Object getSystemService(@ServiceName @NonNull String name);
@@ -2730,6 +2733,15 @@
public static final String USAGE_STATS_SERVICE = "usagestats";
/**
+ * Use with {@link #getSystemService} to retrieve a {@link
+ * android.app.task.TaskManager} instance for managing occasional
+ * background tasks.
+ * @see #getSystemService
+ * @see android.app.task.TaskManager
+ */
+ public static final String TASK_SERVICE = "task";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 076f657..bd07470 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -45,6 +45,7 @@
import android.util.Log;
import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.Serializable;
@@ -604,6 +605,15 @@
* of all possible flags.
*/
public class Intent implements Parcelable, Cloneable {
+ private static final String ATTR_ACTION = "action";
+ private static final String TAG_CATEGORIES = "categories";
+ private static final String ATTR_CATEGORY = "category";
+ private static final String TAG_EXTRA = "extra";
+ private static final String ATTR_TYPE = "type";
+ private static final String ATTR_COMPONENT = "component";
+ private static final String ATTR_DATA = "data";
+ private static final String ATTR_FLAGS = "flags";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent activity actions (see action variable).
@@ -3729,32 +3739,27 @@
*/
public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0x00080000;
/**
- * This flag is used to break out "documents" into separate tasks that can
- * be reached via the Recents mechanism. Such a document is any kind of
- * item for which an application may want to maintain multiple simultaneous
- * instances. Examples might be text files, web pages, spreadsheets, or
- * emails. Each such document will be in a separate task in the Recents list.
+ * This flag is used to open a document into a new task rooted at the activity launched
+ * by this Intent. Through the use of this flag, or its equivalent attribute,
+ * {@link android.R.attr#documentLaunchMode} multiple instances of the same activity
+ * containing different douments will appear in the recent tasks list.
*
- * <p>When set, the activity specified by this Intent will launch into a
- * separate task rooted at that activity. The activity launched must be
- * defined with {@link android.R.attr#launchMode} <code>standard</code>
- * or <code>singleTop</code>.
+ * <p>The use of the activity attribute form of this,
+ * {@link android.R.attr#documentLaunchMode}, is
+ * preferred over the Intent flag described here. The attribute form allows the
+ * Activity to specify multiple document behavior for all launchers of the Activity
+ * whereas using this flag requires each Intent that launches the Activity to specify it.
*
- * <p>If FLAG_ACTIVITY_NEW_DOCUMENT is used without
- * {@link #FLAG_ACTIVITY_MULTIPLE_TASK} then the activity manager will
- * search for an existing task with a matching target activity and Intent
- * data URI and relaunch that task, first finishing all activities down to
- * the root activity and then calling the root activity's
- * {@link android.app.Activity#onNewIntent(Intent)} method. If no existing
- * task's root activity matches the Intent's data URI then a new task will
- * be launched with the target activity as root.
+ * <p>FLAG_ACTIVITY_NEW_DOCUMENT may be used in conjunction with {@link
+ * #FLAG_ACTIVITY_MULTIPLE_TASK}. When used alone it is the
+ * equivalent of the Activity manifest specifying {@link
+ * android.R.attr#documentLaunchMode}="intoExisting". When used with
+ * FLAG_ACTIVITY_MULTIPLE_TASK it is the equivalent of the Activity manifest specifying
+ * {@link android.R.attr#documentLaunchMode}="always".
*
- * <p>When paired with {@link #FLAG_ACTIVITY_MULTIPLE_TASK} this will
- * always create a new task. Thus the same document may be made to appear
- * more than one time in Recents.
+ * Refer to {@link android.R.attr#documentLaunchMode} for more information.
*
- * <p>This is equivalent to the attribute {@link android.R.attr#documentLaunchMode}.
- *
+ * @see android.R.attr#documentLaunchMode
* @see #FLAG_ACTIVITY_MULTIPLE_TASK
*/
public static final int FLAG_ACTIVITY_NEW_DOCUMENT =
@@ -7347,7 +7352,7 @@
}
String nodeName = parser.getName();
- if (nodeName.equals("category")) {
+ if (nodeName.equals(TAG_CATEGORIES)) {
sa = resources.obtainAttributes(attrs,
com.android.internal.R.styleable.IntentCategory);
String cat = sa.getString(com.android.internal.R.styleable.IntentCategory_name);
@@ -7358,11 +7363,11 @@
}
XmlUtils.skipCurrentTag(parser);
- } else if (nodeName.equals("extra")) {
+ } else if (nodeName.equals(TAG_EXTRA)) {
if (intent.mExtras == null) {
intent.mExtras = new Bundle();
}
- resources.parseBundleExtra("extra", attrs, intent.mExtras);
+ resources.parseBundleExtra(TAG_EXTRA, attrs, intent.mExtras);
XmlUtils.skipCurrentTag(parser);
} else {
@@ -7373,6 +7378,76 @@
return intent;
}
+ /** @hide */
+ public void saveToXml(XmlSerializer out) throws IOException {
+ if (mAction != null) {
+ out.attribute(null, ATTR_ACTION, mAction);
+ }
+ if (mData != null) {
+ out.attribute(null, ATTR_DATA, mData.toString());
+ }
+ if (mType != null) {
+ out.attribute(null, ATTR_TYPE, mType);
+ }
+ if (mComponent != null) {
+ out.attribute(null, ATTR_COMPONENT, mComponent.flattenToShortString());
+ }
+ out.attribute(null, ATTR_FLAGS, Integer.toHexString(getFlags()));
+
+ if (mCategories != null) {
+ out.startTag(null, TAG_CATEGORIES);
+ for (int categoryNdx = mCategories.size() - 1; categoryNdx >= 0; --categoryNdx) {
+ out.attribute(null, ATTR_CATEGORY, mCategories.valueAt(categoryNdx));
+ }
+ }
+ }
+
+ /** @hide */
+ public static Intent restoreFromXml(XmlPullParser in) throws IOException,
+ XmlPullParserException {
+ Intent intent = new Intent();
+ final int outerDepth = in.getDepth();
+
+ int attrCount = in.getAttributeCount();
+ for (int attrNdx = attrCount - 1; attrNdx >= 0; --attrNdx) {
+ final String attrName = in.getAttributeName(attrNdx);
+ final String attrValue = in.getAttributeValue(attrNdx);
+ if (ATTR_ACTION.equals(attrName)) {
+ intent.setAction(attrValue);
+ } else if (ATTR_DATA.equals(attrName)) {
+ intent.setData(Uri.parse(attrValue));
+ } else if (ATTR_TYPE.equals(attrName)) {
+ intent.setType(attrValue);
+ } else if (ATTR_COMPONENT.equals(attrName)) {
+ intent.setComponent(ComponentName.unflattenFromString(attrValue));
+ } else if (ATTR_FLAGS.equals(attrName)) {
+ intent.setFlags(Integer.valueOf(attrValue, 16));
+ } else {
+ Log.e("Intent", "restoreFromXml: unknown attribute=" + attrName);
+ }
+ }
+
+ int event;
+ String name;
+ while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+ (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
+ if (event == XmlPullParser.START_TAG) {
+ name = in.getName();
+ if (TAG_CATEGORIES.equals(name)) {
+ attrCount = in.getAttributeCount();
+ for (int attrNdx = attrCount - 1; attrNdx >= 0; --attrNdx) {
+ intent.addCategory(in.getAttributeValue(attrNdx));
+ }
+ } else {
+ Log.w("Intent", "restoreFromXml: unknown name=" + name);
+ XmlUtils.skipCurrentTag(in);
+ }
+ }
+ }
+
+ return intent;
+ }
+
/**
* Normalize a MIME data type.
*
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index 6b4404d..46c9234 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -355,7 +355,14 @@
/**
* Registers a callback to be invoked when a change happens to a preference.
- *
+ *
+ * <p class="caution"><strong>Caution:</strong> The preference manager does
+ * not currently store a strong reference to the listener. You must store a
+ * strong reference to the listener, or it will be susceptible to garbage
+ * collection. We recommend you keep a reference to the listener in the
+ * instance data of an object that will exist as long as you need the
+ * listener.</p>
+ *
* @param listener The callback that will run.
* @see #unregisterOnSharedPreferenceChangeListener
*/
diff --git a/core/java/android/content/Task.java b/core/java/android/content/Task.java
deleted file mode 100644
index 407880f..0000000
--- a/core/java/android/content/Task.java
+++ /dev/null
@@ -1,400 +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.content;
-
-import android.app.task.TaskService;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Container of data passed to the {@link android.content.TaskManager} fully encapsulating the
- * parameters required to schedule work against the calling application. These are constructed
- * using the {@link Task.Builder}.
- */
-public class Task implements Parcelable {
-
- public interface NetworkType {
- public final int ANY = 0;
- public final int UNMETERED = 1;
- }
-
- /**
- * Linear: retry_time(failure_time, t) = failure_time + initial_retry_delay * t, t >= 1
- * Expon: retry_time(failure_time, t) = failure_time + initial_retry_delay ^ t, t >= 1
- */
- public interface BackoffPolicy {
- public final int LINEAR = 0;
- public final int EXPONENTIAL = 1;
- }
-
- private final int taskId;
- // TODO: Change this to use PersistableBundle when that lands in master.
- private final Bundle extras;
- private final ComponentName service;
- private final boolean requireCharging;
- private final boolean requireDeviceIdle;
- private final int networkCapabilities;
- private final long minLatencyMillis;
- private final long maxExecutionDelayMillis;
- private final boolean isPeriodic;
- private final long intervalMillis;
- private final long initialBackoffMillis;
- private final int backoffPolicy;
-
- /**
- * Unique task id associated with this class. This is assigned to your task by the scheduler.
- */
- public int getTaskId() {
- return taskId;
- }
-
- /**
- * Bundle of extras which are returned to your application at execution time.
- */
- public Bundle getExtras() {
- return extras;
- }
-
- /**
- * Name of the service endpoint that will be called back into by the TaskManager.
- */
- public ComponentName getService() {
- return service;
- }
-
- /**
- * Whether this task needs the device to be plugged in.
- */
- public boolean isRequireCharging() {
- return requireCharging;
- }
-
- /**
- * Whether this task needs the device to be in an Idle maintenance window.
- */
- public boolean isRequireDeviceIdle() {
- return requireDeviceIdle;
- }
-
- /**
- * See {@link android.content.Task.NetworkType} for a description of this value.
- */
- public int getNetworkCapabilities() {
- return networkCapabilities;
- }
-
- /**
- * Set for a task that does not recur periodically, to specify a delay after which the task
- * will be eligible for execution. This value is not set if the task recurs periodically.
- */
- public long getMinLatencyMillis() {
- return minLatencyMillis;
- }
-
- /**
- * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the task recurs
- * periodically.
- */
- public long getMaxExecutionDelayMillis() {
- return maxExecutionDelayMillis;
- }
-
- /**
- * Track whether this task will repeat with a given period.
- */
- public boolean isPeriodic() {
- return isPeriodic;
- }
-
- /**
- * Set to the interval between occurrences of this task. This value is <b>not</b> set if the
- * task does not recur periodically.
- */
- public long getIntervalMillis() {
- return intervalMillis;
- }
-
- /**
- * The amount of time the TaskManager will wait before rescheduling a failed task. This value
- * will be increased depending on the backoff policy specified at task creation time. Defaults
- * to 5 seconds.
- */
- public long getInitialBackoffMillis() {
- return initialBackoffMillis;
- }
-
- /**
- * See {@link android.content.Task.BackoffPolicy} for an explanation of the values this field
- * can take. This defaults to exponential.
- */
- public int getBackoffPolicy() {
- return backoffPolicy;
- }
-
- private Task(Parcel in) {
- taskId = in.readInt();
- extras = in.readBundle();
- service = ComponentName.readFromParcel(in);
- requireCharging = in.readInt() == 1;
- requireDeviceIdle = in.readInt() == 1;
- networkCapabilities = in.readInt();
- minLatencyMillis = in.readLong();
- maxExecutionDelayMillis = in.readLong();
- isPeriodic = in.readInt() == 1;
- intervalMillis = in.readLong();
- initialBackoffMillis = in.readLong();
- backoffPolicy = in.readInt();
- }
-
- private Task(Task.Builder b) {
- taskId = b.mTaskId;
- extras = new Bundle(b.mExtras);
- service = b.mTaskService;
- requireCharging = b.mRequiresCharging;
- requireDeviceIdle = b.mRequiresDeviceIdle;
- networkCapabilities = b.mNetworkCapabilities;
- minLatencyMillis = b.mMinLatencyMillis;
- maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
- isPeriodic = b.mIsPeriodic;
- intervalMillis = b.mIntervalMillis;
- initialBackoffMillis = b.mInitialBackoffMillis;
- backoffPolicy = b.mBackoffPolicy;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(taskId);
- out.writeBundle(extras);
- ComponentName.writeToParcel(service, out);
- out.writeInt(requireCharging ? 1 : 0);
- out.writeInt(requireDeviceIdle ? 1 : 0);
- out.writeInt(networkCapabilities);
- out.writeLong(minLatencyMillis);
- out.writeLong(maxExecutionDelayMillis);
- out.writeInt(isPeriodic ? 1 : 0);
- out.writeLong(intervalMillis);
- out.writeLong(initialBackoffMillis);
- out.writeInt(backoffPolicy);
- }
-
- public static final Creator<Task> CREATOR = new Creator<Task>() {
- @Override
- public Task createFromParcel(Parcel in) {
- return new Task(in);
- }
-
- @Override
- public Task[] newArray(int size) {
- return new Task[size];
- }
- };
-
- /**
- * Builder class for constructing {@link Task} objects.
- */
- public final class Builder {
- private int mTaskId;
- private Bundle mExtras;
- private ComponentName mTaskService;
- // Requirements.
- private boolean mRequiresCharging;
- private boolean mRequiresDeviceIdle;
- private int mNetworkCapabilities;
- // One-off parameters.
- private long mMinLatencyMillis;
- private long mMaxExecutionDelayMillis;
- // Periodic parameters.
- private boolean mIsPeriodic;
- private long mIntervalMillis;
- // Back-off parameters.
- private long mInitialBackoffMillis = 5000L;
- private int mBackoffPolicy = BackoffPolicy.EXPONENTIAL;
- /** Easy way to track whether the client has tried to set a back-off policy. */
- private boolean mBackoffPolicySet = false;
-
- /**
- * @param taskId Application-provided id for this task. Subsequent calls to cancel, or
- * tasks created with the same taskId, will update the pre-existing task with
- * the same id.
- * @param taskService The endpoint that you implement that will receive the callback from the
- * TaskManager.
- */
- public Builder(int taskId, ComponentName taskService) {
- mTaskService = taskService;
- mTaskId = taskId;
- }
-
- /**
- * Set optional extras. This is persisted, so we only allow primitive types.
- * @param extras Bundle containing extras you want the scheduler to hold on to for you.
- */
- public Builder setExtras(Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Set some description of the kind of network capabilities you would like to have. This
- * will be a parameter defined in {@link android.content.Task.NetworkType}.
- * Not calling this function means the network is not necessary.
- * Bear in mind that calling this function defines network as a strict requirement for your
- * task if the network requested is not available your task will never run. See
- * {@link #setOverrideDeadline(long)} to change this behaviour.
- */
- public Builder setRequiredNetworkCapabilities(int networkCapabilities) {
- mNetworkCapabilities = networkCapabilities;
- return this;
- }
-
- /*
- * Specify that to run this task, the device needs to be plugged in. This defaults to
- * false.
- * @param requireCharging Whether or not the device is plugged in.
- */
- public Builder setRequiresCharging(boolean requiresCharging) {
- mRequiresCharging = requiresCharging;
- return this;
- }
-
- /**
- * Specify that to run, the task needs the device to be in idle mode. This defaults to
- * false.
- * <p>Idle mode is a loose definition provided by the system, which means that the device
- * is not in use, and has not been in use for some time. As such, it is a good time to
- * perform resource heavy tasks. Bear in mind that battery usage will still be attributed
- * to your application, and surfaced to the user in battery stats.</p>
- * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance
- * window.
- */
- public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
- mRequiresDeviceIdle = requiresDeviceIdle;
- return this;
- }
-
- /**
- * Specify that this task should recur with the provided interval, not more than once per
- * period. You have no control over when within this interval this task will be executed,
- * only the guarantee that it will be executed at most once within this interval.
- * A periodic task will be repeated until the phone is turned off, however it will only be
- * persisted beyond boot if the client app has declared the
- * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission. You can schedule
- * periodic tasks without this permission, they simply will cease to exist after the phone
- * restarts.
- * Setting this function on the builder with {@link #setMinimumLatency(long)} or
- * {@link #setOverrideDeadline(long)} will result in an error.
- * @param intervalMillis Millisecond interval for which this task will repeat.
- */
- public Builder setPeriodic(long intervalMillis) {
- mIsPeriodic = true;
- mIntervalMillis = intervalMillis;
- return this;
- }
-
- /**
- * Specify that this task should be delayed by the provided amount of time.
- * Because it doesn't make sense setting this property on a periodic task, doing so will
- * throw an {@link java.lang.IllegalArgumentException} when
- * {@link android.content.Task.Builder#build()} is called.
- * @param minLatencyMillis Milliseconds before which this task will not be considered for
- * execution.
- */
- public Builder setMinimumLatency(long minLatencyMillis) {
- mMinLatencyMillis = minLatencyMillis;
- return this;
- }
-
- /**
- * Set deadline which is the maximum scheduling latency. The task will be run by this
- * deadline even if other requirements are not met. Because it doesn't make sense setting
- * this property on a periodic task, doing so will throw an
- * {@link java.lang.IllegalArgumentException} when
- * {@link android.content.Task.Builder#build()} is called.
- */
- public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
- mMaxExecutionDelayMillis = maxExecutionDelayMillis;
- return this;
- }
-
- /**
- * Set up the back-off/retry policy.
- * This defaults to some respectable values: {5 seconds, Exponential}. We cap back-off at
- * 1hr.
- * Note that trying to set a backoff criteria for a task with
- * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build().
- * This is because back-off typically does not make sense for these types of tasks. See
- * {@link android.app.task.TaskService#taskFinished(android.app.task.TaskParams, boolean)}
- * for more description of the return value for the case of a task executing while in idle
- * mode.
- * @param initialBackoffMillis Millisecond time interval to wait initially when task has
- * failed.
- * @param backoffPolicy is one of {@link BackoffPolicy}
- */
- public Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) {
- mBackoffPolicySet = true;
- mInitialBackoffMillis = initialBackoffMillis;
- mBackoffPolicy = backoffPolicy;
- return this;
- }
-
- /**
- * @return The task object to hand to the TaskManager. This object is immutable.
- */
- public Task build() {
- // Check that extras bundle only contains primitive types.
- try {
- for (String key : extras.keySet()) {
- Object value = extras.get(key);
- if (value == null) continue;
- if (value instanceof Long) continue;
- if (value instanceof Integer) continue;
- if (value instanceof Boolean) continue;
- if (value instanceof Float) continue;
- if (value instanceof Double) continue;
- if (value instanceof String) continue;
- throw new IllegalArgumentException("Unexpected value type: "
- + value.getClass().getName());
- }
- } catch (IllegalArgumentException e) {
- throw e;
- } catch (RuntimeException exc) {
- throw new IllegalArgumentException("error unparcelling Bundle", exc);
- }
- // Check that a deadline was not set on a periodic task.
- if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) {
- throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
- "periodic task.");
- }
- if (mIsPeriodic && (mMinLatencyMillis != 0L)) {
- throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
- "periodic task");
- }
- if (mBackoffPolicySet && mRequiresDeviceIdle) {
- throw new IllegalArgumentException("An idle mode task will not respect any" +
- " back-off policy, so calling setBackoffCriteria with" +
- " setRequiresDeviceIdle is an error.");
- }
- return new Task(this);
- }
- }
-
-}
diff --git a/core/java/android/content/TaskManager.java b/core/java/android/content/TaskManager.java
deleted file mode 100644
index d28d78a..0000000
--- a/core/java/android/content/TaskManager.java
+++ /dev/null
@@ -1,66 +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.content;
-
-import java.util.List;
-
-/**
- * Class for scheduling various types of tasks with the scheduling framework on the device.
- *
- * Get an instance of this class through {@link Context#getSystemService(String)}.
- */
-public abstract class TaskManager {
- /*
- * Returned from {@link #schedule(Task)} when an invalid parameter was supplied. This can occur
- * if the run-time for your task is too short, or perhaps the system can't resolve the
- * requisite {@link TaskService} in your package.
- */
- static final int RESULT_INVALID_PARAMETERS = -1;
- /**
- * Returned from {@link #schedule(Task)} if this application has made too many requests for
- * work over too short a time.
- */
- // TODO: Determine if this is necessary.
- static final int RESULT_OVER_QUOTA = -2;
-
- /*
- * @param task The task you wish scheduled. See {@link Task#TaskBuilder} for more detail on
- * the sorts of tasks you can schedule.
- * @return If >0, this int corresponds to the taskId of the successfully scheduled task.
- * Otherwise you have to compare the return value to the error codes defined in this class.
- */
- public abstract int schedule(Task task);
-
- /**
- * Cancel a task that is pending in the TaskManager.
- * @param taskId unique identifier for this task. Obtain this value from the tasks returned by
- * {@link #getAllPendingTasks()}.
- * @return
- */
- public abstract void cancel(int taskId);
-
- /**
- * Cancel all tasks that have been registered with the TaskManager by this package.
- */
- public abstract void cancelAll();
-
- /**
- * @return a list of all the tasks registered by this package that have not yet been executed.
- */
- public abstract List<Task> getAllPendingTasks();
-
-}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 6cb781f..44a6a5d 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -112,7 +112,7 @@
ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId);
- boolean canForwardTo(in Intent intent, String resolvedType, int userIdFrom, int userIdDest);
+ boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId);
List<ResolveInfo> queryIntentActivities(in Intent intent,
String resolvedType, int flags, int userId);
@@ -248,10 +248,10 @@
void clearPackagePersistentPreferredActivities(String packageName, int userId);
- void addForwardingIntentFilter(in IntentFilter filter, boolean removable, int userIdOrig,
- int userIdDest);
+ void addCrossProfileIntentFilter(in IntentFilter filter, boolean removable, int sourceUserId,
+ int targetUserId);
- void clearForwardingIntentFilters(int userIdOrig);
+ void clearCrossProfileIntentFilters(int sourceUserId);
/**
* Report the set of 'Home' activity candidates, plus (if any) which of them
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d7bd473..4672015 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -19,9 +19,12 @@
import android.app.PackageInstallObserver;
import android.app.PackageUninstallObserver;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.FileBridge;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import java.io.OutputStream;
+
/** {@hide} */
public class PackageInstaller {
private final PackageManager mPm;
@@ -127,10 +130,17 @@
}
}
- public ParcelFileDescriptor openWrite(String overlayName, long offsetBytes,
- long lengthBytes) {
+ /**
+ * Open an APK file for writing, starting at the given offset. You can
+ * then stream data into the file, periodically calling
+ * {@link OutputStream#flush()} to ensure bytes have been written to
+ * disk.
+ */
+ public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes) {
try {
- return mSession.openWrite(overlayName, offsetBytes, lengthBytes);
+ final ParcelFileDescriptor clientSocket = mSession.openWrite(splitName,
+ offsetBytes, lengthBytes);
+ return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 35bcc02..c5cd5c9 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3576,24 +3576,38 @@
}
/**
- * Adds a forwarding intent filter. After calling this method all intents sent from the user
- * with id userIdOrig can also be be resolved by activities in the user with id userIdDest if
- * they match the specified intent filter.
- * @param filter the {@link IntentFilter} the intent has to match to be forwarded
- * @param removable if set to false, {@link clearForwardingIntents} will not remove this intent
- * filter
- * @param userIdOrig user from which the intent can be forwarded
- * @param userIdDest user to which the intent can be forwarded
+ * Adds a {@link CrossProfileIntentFilter}. After calling this method all intents sent from the
+ * user with id sourceUserId can also be be resolved by activities in the user with id
+ * targetUserId if they match the specified intent filter.
+ * @param filter the {@link IntentFilter} the intent has to match
+ * @param removable if set to false, {@link clearCrossProfileIntentFilters} will not remove this
+ * {@link CrossProfileIntentFilter}
* @hide
*/
- public abstract void addForwardingIntentFilter(IntentFilter filter, boolean removable,
- int userIdOrig, int userIdDest);
+ public abstract void addCrossProfileIntentFilter(IntentFilter filter, boolean removable,
+ int sourceUserId, int targetUserId);
/**
- * Clearing all removable {@link ForwardingIntentFilter}s that are set with the given user as
- * the origin.
- * @param userIdOrig user from which the intent can be forwarded
+ * @hide
+ * @deprecated
+ * TODO: remove it as soon as the code of ManagedProvisionning is updated
+ */
+ public abstract void addForwardingIntentFilter(IntentFilter filter, boolean removable,
+ int sourceUserId, int targetUserId);
+
+ /**
+ * Clearing removable {@link CrossProfileIntentFilter}s which have the specified user as their
+ * source
+ * @param sourceUserId
+ * be cleared.
* @hide
*/
- public abstract void clearForwardingIntentFilters(int userIdOrig);
+ public abstract void clearCrossProfileIntentFilters(int sourceUserId);
+
+ /**
+ * @hide
+ * @deprecated
+ * TODO: remove it as soon as the code of ManagedProvisionning is updated
+ */
+ public abstract void clearForwardingIntentFilters(int sourceUserId);
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index a78f8e2..3737638 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -31,11 +31,11 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Trace;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.TypedValue;
import android.util.LongSparseArray;
@@ -104,10 +104,10 @@
// These are protected by mAccessLock.
private final Object mAccessLock = new Object();
private final Configuration mTmpConfig = new Configuration();
- private final ThemedCaches<ConstantState> mDrawableCache =
- new ThemedCaches<ConstantState>();
- private final ThemedCaches<ConstantState> mColorDrawableCache =
- new ThemedCaches<ConstantState>();
+ private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mDrawableCache =
+ new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
+ private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mColorDrawableCache =
+ new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
private final LongSparseArray<WeakReference<ColorStateList>> mColorStateListCache =
new LongSparseArray<WeakReference<ColorStateList>>();
@@ -1261,18 +1261,17 @@
* any of the style's attributes are already defined in the theme, the
* current values in the theme will be overwritten.
*
- * @param resid The resource ID of a style resource from which to
+ * @param resId The resource ID of a style resource from which to
* obtain attribute values.
* @param force If true, values in the style resource will always be
* used in the theme; otherwise, they will only be used
* if not already defined in the theme.
*/
- public void applyStyle(int resid, boolean force) {
- AssetManager.applyThemeStyle(mTheme, resid, force);
+ public void applyStyle(int resId, boolean force) {
+ AssetManager.applyThemeStyle(mTheme, resId, force);
- // TODO: In very rare cases, we may end up with a hybrid theme
- // that can't map to a single theme ID.
- mThemeResId = resid;
+ mThemeResId = resId;
+ mKey += Integer.toHexString(resId) + (force ? "! " : " ");
}
/**
@@ -1288,6 +1287,7 @@
AssetManager.copyTheme(mTheme, other.mTheme);
mThemeResId = other.mThemeResId;
+ mKey = other.mKey;
}
/**
@@ -1577,6 +1577,9 @@
/** Resource identifier for the theme. */
private int mThemeResId = 0;
+ /** Unique key for the series of styles applied to this theme. */
+ private String mKey = "";
+
// Needed by layoutlib.
/*package*/ long getNativeTheme() {
return mTheme;
@@ -1585,6 +1588,10 @@
/*package*/ int getAppliedStyleResId() {
return mThemeResId;
}
+
+ /*package*/ String getKey() {
+ return mKey;
+ }
}
/**
@@ -1740,7 +1747,8 @@
}
private void clearDrawableCachesLocked(
- ThemedCaches<ConstantState> caches, int configChanges) {
+ ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
+ int configChanges) {
final int N = caches.size();
for (int i = 0; i < N; i++) {
clearDrawableCacheLocked(caches.valueAt(i), configChanges);
@@ -1763,7 +1771,7 @@
configChanges, cs.getChangingConfigurations())) {
if (DEBUG_CONFIG) {
Log.d(TAG, "FLUSHING #0x"
- + Long.toHexString(mDrawableCache.keyAt(i))
+ + Long.toHexString(cache.keyAt(i))
+ " / " + cs + " with changes: 0x"
+ Integer.toHexString(cs.getChangingConfigurations()));
}
@@ -2205,7 +2213,7 @@
}
final boolean isColorDrawable;
- final ThemedCaches<ConstantState> caches;
+ final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches;
final long key;
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
&& value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
@@ -2258,7 +2266,8 @@
}
private void cacheDrawable(TypedValue value, Theme theme, boolean isColorDrawable,
- ThemedCaches<ConstantState> caches, long key, Drawable dr) {
+ ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
+ long key, Drawable dr) {
final ConstantState cs = dr.getConstantState();
if (cs == null) {
return;
@@ -2287,8 +2296,12 @@
}
} else {
synchronized (mAccessLock) {
- final LongSparseArray<WeakReference<ConstantState>> themedCache;
- themedCache = caches.getOrCreate(theme == null ? 0 : theme.mThemeResId);
+ final String themeKey = theme == null ? "" : theme.mKey;
+ LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey);
+ if (themedCache == null) {
+ themedCache = new LongSparseArray<WeakReference<ConstantState>>(1);
+ caches.put(themeKey, themedCache);
+ }
themedCache.put(key, new WeakReference<ConstantState>(cs));
}
}
@@ -2347,7 +2360,9 @@
return dr;
}
- private Drawable getCachedDrawable(ThemedCaches<ConstantState> caches, long key, Theme theme) {
+ private Drawable getCachedDrawable(
+ ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
+ long key, Theme theme) {
synchronized (mAccessLock) {
final int themeKey = theme != null ? theme.mThemeResId : 0;
final LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey);
@@ -2584,21 +2599,4 @@
updateConfiguration(null, null);
mAssets.ensureStringBlocks();
}
-
- static class ThemedCaches<T> extends SparseArray<LongSparseArray<WeakReference<T>>> {
- /**
- * Returns the cache of drawables styled for the specified theme.
- * <p>
- * Drawables that have themeable attributes but were loaded without
- * specifying a theme are cached at themeResId = 0.
- */
- public LongSparseArray<WeakReference<T>> getOrCreate(int themeResId) {
- LongSparseArray<WeakReference<T>> result = get(themeResId);
- if (result == null) {
- result = new LongSparseArray<WeakReference<T>>(1);
- put(themeResId, result);
- }
- return result;
- }
- }
}
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index 220b40d..2dce425 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -22,6 +22,7 @@
import android.util.Log;
import android.os.Debug;
import android.os.UserHandle;
+import dalvik.system.VMRuntime;
import java.nio.ByteBuffer;
@@ -126,8 +127,21 @@
// appName = "unknown";
String appName = DdmHandleAppName.getAppName();
- ByteBuffer out = ByteBuffer.allocate(20
- + vmIdent.length()*2 + appName.length()*2);
+ VMRuntime vmRuntime = VMRuntime.getRuntime();
+ String instructionSetDescription =
+ vmRuntime.is64Bit() ? "64-bit" : "32-bit";
+ String vmInstructionSet = vmRuntime.vmInstructionSet();
+ if (vmInstructionSet != null && vmInstructionSet.length() > 0) {
+ instructionSetDescription += " (" + vmInstructionSet + ")";
+ }
+ String vmFlags = "CheckJNI="
+ + (vmRuntime.isCheckJniEnabled() ? "true" : "false");
+
+ ByteBuffer out = ByteBuffer.allocate(28
+ + vmIdent.length() * 2
+ + appName.length() * 2
+ + instructionSetDescription.length() * 2
+ + vmFlags.length() * 2);
out.order(ChunkHandler.CHUNK_ORDER);
out.putInt(DdmServer.CLIENT_PROTOCOL_VERSION);
out.putInt(android.os.Process.myPid());
@@ -136,6 +150,10 @@
putString(out, vmIdent);
putString(out, appName);
out.putInt(UserHandle.myUserId());
+ out.putInt(instructionSetDescription.length());
+ putString(out, instructionSetDescription);
+ out.putInt(vmFlags.length());
+ putString(out, vmFlags);
Chunk reply = new Chunk(CHUNK_HELO, out);
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 86208fc..54baaeb 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -381,11 +381,17 @@
/**
* A constant describing a heart rate monitor.
* <p>
- * A sensor that measures the heart rate in beats per minute.
+ * The reported value is the heart rate in beats per minute.
* <p>
- * value[0] represents the beats per minute when the measurement was taken.
- * value[0] is 0 if the heart rate monitor could not measure the rate or the
- * rate is 0 beat per minute.
+ * The reported accuracy represents the status of the monitor during the reading. See the
+ * {@code SENSOR_STATUS_*} constants in {@link android.hardware.SensorManager SensorManager}
+ * for more details on accuracy/status values. In particular, when the accuracy is
+ * {@code SENSOR_STATUS_UNRELIABLE} or {@code SENSOR_STATUS_NO_CONTACT}, the heart rate
+ * value should be discarded.
+ * <p>
+ * This sensor requires permission {@code android.permission.BODY_SENSORS}.
+ * It will not be returned by {@code SensorManager.getSensorsList} nor
+ * {@code SensorManager.getDefaultSensor} if the application doesn't have this permission.
*/
public static final int TYPE_HEART_RATE = 21;
@@ -613,6 +619,7 @@
}
/**
+ * @hide
* @return The permission required to access this sensor. If empty, no permission is required.
*/
public String getRequiredPermission() {
diff --git a/core/java/android/hardware/SensorEventListener.java b/core/java/android/hardware/SensorEventListener.java
index 677d244..0d859fb9 100644
--- a/core/java/android/hardware/SensorEventListener.java
+++ b/core/java/android/hardware/SensorEventListener.java
@@ -39,11 +39,13 @@
public void onSensorChanged(SensorEvent event);
/**
- * Called when the accuracy of a sensor has changed.
- * <p>See {@link android.hardware.SensorManager SensorManager}
- * for details.
+ * Called when the accuracy of the registered sensor has changed.
*
- * @param accuracy The new accuracy of this sensor
+ * <p>See the SENSOR_STATUS_* constants in
+ * {@link android.hardware.SensorManager SensorManager} for details.
+ *
+ * @param accuracy The new accuracy of this sensor, one of
+ * {@code SensorManager.SENSOR_STATUS_*}
*/
- public void onAccuracyChanged(Sensor sensor, int accuracy);
+ public void onAccuracyChanged(Sensor sensor, int accuracy);
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 5f2b5f0..25c7630 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -321,6 +321,13 @@
/**
+ * The values returned by this sensor cannot be trusted because the sensor
+ * had no contact with what it was measuring (for example, the heart rate
+ * monitor is not in contact with the user).
+ */
+ public static final int SENSOR_STATUS_NO_CONTACT = -1;
+
+ /**
* The values returned by this sensor cannot be trusted, calibration is
* needed or the environment doesn't allow readings
*/
@@ -421,9 +428,10 @@
* {@link SensorManager#getSensorList(int) getSensorList}.
*
* @param type
- * of sensors requested
+ * of sensors requested
*
- * @return the default sensors matching the asked type.
+ * @return the default sensor matching the requested type if one exists and the application
+ * has the necessary permissions, or null otherwise.
*
* @see #getSensorList(int)
* @see Sensor
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 8684a04..b66ec86 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -395,25 +395,12 @@
t.timestamp = timestamp;
t.accuracy = inAccuracy;
t.sensor = sensor;
- switch (t.sensor.getType()) {
- // Only report accuracy for sensors that support it.
- case Sensor.TYPE_MAGNETIC_FIELD:
- case Sensor.TYPE_ORIENTATION:
- // call onAccuracyChanged() only if the value changes
- final int accuracy = mSensorAccuracies.get(handle);
- if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
- mSensorAccuracies.put(handle, t.accuracy);
- mListener.onAccuracyChanged(t.sensor, t.accuracy);
- }
- break;
- default:
- // For other sensors, just report the accuracy once
- if (mFirstEvent.get(handle) == false) {
- mFirstEvent.put(handle, true);
- mListener.onAccuracyChanged(
- t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
- }
- break;
+
+ // call onAccuracyChanged() only if the value changes
+ final int accuracy = mSensorAccuracies.get(handle);
+ if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
+ mSensorAccuracies.put(handle, t.accuracy);
+ mListener.onAccuracyChanged(t.sensor, t.accuracy);
}
mListener.onSensorChanged(t);
}
diff --git a/core/java/android/hardware/camera2/CameraAccessException.java b/core/java/android/hardware/camera2/CameraAccessException.java
index 1af575f..ca71e81 100644
--- a/core/java/android/hardware/camera2/CameraAccessException.java
+++ b/core/java/android/hardware/camera2/CameraAccessException.java
@@ -114,7 +114,10 @@
mReason = problem;
}
- private static String getDefaultMessage(int problem) {
+ /**
+ * @hide
+ */
+ public static String getDefaultMessage(int problem) {
switch (problem) {
case CAMERA_IN_USE:
return "The camera device is in use already";
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
new file mode 100644
index 0000000..8391209
--- /dev/null
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -0,0 +1,588 @@
+/*
+ * 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;
+
+import android.os.Handler;
+import java.util.List;
+
+/**
+ * A configured capture session for a {@link CameraDevice}, used for capturing
+ * images from the camera.
+ *
+ * <p>A CameraCaptureSession is created by providing a set of target output surfaces to
+ * {@link CameraDevice#createCaptureSession createCaptureSession}. Once created, the session is
+ * active until a new session is created by the camera device, or the camera device is closed.</p>
+ *
+ * <p>Creating a session is an expensive operation and can take several hundred milliseconds, since
+ * it requires configuring the camera device's internal pipelines and allocating memory buffers for
+ * sending images to the desired targets. While
+ * {@link CameraDevice#createCaptureSession createCaptureSession} will provide a
+ * CameraCaptureSession object immediately, configuration won't be complete until the
+ * {@link CameraCaptureSession.StateListener#onConfigured onConfigured} callback is called for the
+ * first time. If configuration cannot be completed, then the
+ * {@link CameraCaptureSession.StateListener#onConfigureFailed onConfigureFailed} is called, and the
+ * session will not become active.</p>
+ *
+ * <p>Any capture requests (repeating or non-repeating) submitted before the session is ready will
+ * be queued up and will begin capture once the session becomes ready. In case the session cannot be
+ * configured and {@link StateListener#onConfigureFailed onConfigureFailed} is called, all queued
+ * capture requests are discarded.</p>
+ *
+ * <p>If a new session is created by the camera device, then the previous session is closed, and its
+ * associated {@link StateListener#onClosed onClosed} callback will be invoked. All
+ * of the session methods will throw an IllegalStateException if called once the session is
+ * closed.</p>
+ *
+ * <p>A closed session clears any repeating requests (as if {@link #stopRepeating} had been called),
+ * but will still complete all of its in-progress capture requests as normal, before a newly
+ * created session takes over and reconfigures the camera device.</p>
+ */
+public abstract class CameraCaptureSession implements AutoCloseable {
+
+ /**
+ * Get the camera device that this session is created for
+ */
+ public abstract CameraDevice getDevice();
+
+ /**
+ * <p>Submit a request for an image to be captured by the camera device.</p>
+ *
+ * <p>The request defines all the parameters for capturing the single image,
+ * including sensor, lens, flash, and post-processing settings.</p>
+ *
+ * <p>Each request will produce one {@link CaptureResult} and produce new frames for one or more
+ * target Surfaces, set with the CaptureRequest builder's
+ * {@link CaptureRequest.Builder#addTarget} method. The target surfaces (set with
+ * {@link CaptureRequest.Builder#addTarget}) must be a subset of the surfaces provided when this
+ * capture session was created.</p>
+ *
+ * <p>Multiple requests can be in progress at once. They are processed in
+ * first-in, first-out order, with minimal delays between each
+ * capture. Requests submitted through this method have higher priority than
+ * those submitted through {@link #setRepeatingRequest} or
+ * {@link #setRepeatingBurst}, and will be processed as soon as the current
+ * repeat/repeatBurst processing completes.</p>
+ *
+ * @param request the settings for this capture
+ * @param listener The callback object to notify once this request has been
+ * processed. If null, no metadata will be produced for this capture,
+ * although image data will still be produced.
+ * @param handler the handler on which the listener should be invoked, or
+ * {@code null} to use the current thread's {@link android.os.Looper
+ * looper}.
+ *
+ * @return int A unique capture sequence ID used by
+ * {@link CaptureListener#onCaptureSequenceCompleted}.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if this session is no longer active, either because a new
+ * session has been created or the camera device has been closed.
+ * @throws IllegalArgumentException if the request targets Surfaces that are not configured as
+ * outputs for this session. Or if the handler is null, the
+ * listener is not null, and the calling thread has no looper.
+ *
+ * @see #captureBurst
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ */
+ public abstract int capture(CaptureRequest request, CaptureListener listener, Handler handler)
+ throws CameraAccessException;
+
+ /**
+ * Submit a list of requests to be captured in sequence as a burst. The
+ * burst will be captured in the minimum amount of time possible, and will
+ * not be interleaved with requests submitted by other capture or repeat
+ * calls.
+ *
+ * <p>The requests will be captured in order, each capture producing one {@link CaptureResult}
+ * and image buffers for one or more target {@link android.view.Surface surfaces}. The target
+ * surfaces (set with {@link CaptureRequest.Builder#addTarget}) must be a subset of the surfaces
+ * provided when this capture session was created.</p>
+ *
+ * <p>The main difference between this method and simply calling
+ * {@link #capture} repeatedly is that this method guarantees that no
+ * other requests will be interspersed with the burst.</p>
+ *
+ * @param requests the list of settings for this burst capture
+ * @param listener The callback object to notify each time one of the
+ * requests in the burst has been processed. If null, no metadata will be
+ * produced for any requests in this burst, although image data will still
+ * be produced.
+ * @param handler the handler on which the listener should be invoked, or
+ * {@code null} to use the current thread's {@link android.os.Looper
+ * looper}.
+ *
+ * @return int A unique capture sequence ID used by
+ * {@link CaptureListener#onCaptureSequenceCompleted}.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if this session is no longer active, either because a new
+ * session has been created or the camera device has been closed.
+ * @throws IllegalArgumentException If the requests target Surfaces not currently configured as
+ * outputs. Or if the handler is null, the listener is not
+ * null, and the calling thread has no looper.
+ *
+ * @see #capture
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ */
+ public abstract int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
+ Handler handler) throws CameraAccessException;
+
+ /**
+ * Request endlessly repeating capture of images by this capture session.
+ *
+ * <p>With this method, the camera device will continually capture images
+ * using the settings in the provided {@link CaptureRequest}, at the maximum
+ * rate possible.</p>
+ *
+ * <p>Repeating requests are a simple way for an application to maintain a
+ * preview or other continuous stream of frames, without having to
+ * continually submit identical requests through {@link #capture}.</p>
+ *
+ * <p>Repeat requests have lower priority than those submitted
+ * through {@link #capture} or {@link #captureBurst}, so if
+ * {@link #capture} is called when a repeating request is active, the
+ * capture request will be processed before any further repeating
+ * requests are processed.<p>
+ *
+ * <p>Repeating requests are a simple way for an application to maintain a
+ * preview or other continuous stream of frames, without having to submit
+ * requests through {@link #capture} at video rates.</p>
+ *
+ * <p>To stop the repeating capture, call {@link #stopRepeating}. Calling
+ * {@link #abortCaptures} will also clear the request.</p>
+ *
+ * <p>Calling this method will replace any earlier repeating request or
+ * burst set up by this method or {@link #setRepeatingBurst}, although any
+ * in-progress burst will be completed before the new repeat request will be
+ * used.</p>
+ *
+ * @param request the request to repeat indefinitely
+ * @param listener The callback object to notify every time the
+ * request finishes processing. If null, no metadata will be
+ * produced for this stream of requests, although image data will
+ * still be produced.
+ * @param handler the handler on which the listener should be invoked, or
+ * {@code null} to use the current thread's {@link android.os.Looper
+ * looper}.
+ *
+ * @return int A unique capture sequence ID used by
+ * {@link CaptureListener#onCaptureSequenceCompleted}.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if this session is no longer active, either because a new
+ * session has been created or the camera device has been closed.
+ * @throws IllegalArgumentException If the requests reference Surfaces that are not currently
+ * configured as outputs. Or if the handler is null, the
+ * listener is not null, and the calling thread has no looper.
+ *
+ * @see #capture
+ * @see #captureBurst
+ * @see #setRepeatingBurst
+ * @see #stopRepeating
+ * @see #abortCaptures
+ */
+ public abstract int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
+ Handler handler) throws CameraAccessException;
+
+ /**
+ * <p>Request endlessly repeating capture of a sequence of images by this
+ * capture session.</p>
+ *
+ * <p>With this method, the camera device will continually capture images,
+ * cycling through the settings in the provided list of
+ * {@link CaptureRequest CaptureRequests}, at the maximum rate possible.</p>
+ *
+ * <p>If a request is submitted through {@link #capture} or
+ * {@link #captureBurst}, the current repetition of the request list will be
+ * completed before the higher-priority request is handled. This guarantees
+ * that the application always receives a complete repeat burst captured in
+ * minimal time, instead of bursts interleaved with higher-priority
+ * captures, or incomplete captures.</p>
+ *
+ * <p>Repeating burst requests are a simple way for an application to
+ * maintain a preview or other continuous stream of frames where each
+ * request is different in a predicatable way, without having to continually
+ * submit requests through {@link #captureBurst}.</p>
+ *
+ * <p>To stop the repeating capture, call {@link #stopRepeating}. Any
+ * ongoing burst will still be completed, however. Calling
+ * {@link #abortCaptures} will also clear the request.</p>
+ *
+ * <p>Calling this method will replace a previously-set repeating request or
+ * burst set up by this method or {@link #setRepeatingRequest}, although any
+ * in-progress burst will be completed before the new repeat burst will be
+ * used.</p>
+ *
+ * @param requests the list of requests to cycle through indefinitely
+ * @param listener The callback object to notify each time one of the
+ * requests in the repeating bursts has finished processing. If null, no
+ * metadata will be produced for this stream of requests, although image
+ * data will still be produced.
+ * @param handler the handler on which the listener should be invoked, or
+ * {@code null} to use the current thread's {@link android.os.Looper
+ * looper}.
+ *
+ * @return int A unique capture sequence ID used by
+ * {@link CaptureListener#onCaptureSequenceCompleted}.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if this session is no longer active, either because a new
+ * session has been created or the camera device has been closed.
+ * @throws IllegalArgumentException If the requests reference Surfaces not currently configured
+ * as outputs. Or if the handler is null, the listener is not
+ * null, and the calling thread has no looper.
+ *
+ * @see #capture
+ * @see #captureBurst
+ * @see #setRepeatingRequest
+ * @see #stopRepeating
+ * @see #abortCaptures
+ */
+ public abstract int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
+ Handler handler) throws CameraAccessException;
+
+ /**
+ * <p>Cancel any ongoing repeating capture set by either
+ * {@link #setRepeatingRequest setRepeatingRequest} or
+ * {@link #setRepeatingBurst}. Has no effect on requests submitted through
+ * {@link #capture capture} or {@link #captureBurst captureBurst}.</p>
+ *
+ * <p>Any currently in-flight captures will still complete, as will any burst that is
+ * mid-capture. To ensure that the device has finished processing all of its capture requests
+ * and is in ready state, wait for the {@link StateListener#onReady} callback after
+ * calling this method.</p>
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if this session is no longer active, either because a new
+ * session has been created or the camera device has been closed.
+ *
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ * @see StateListener#onIdle
+ */
+ public abstract void stopRepeating() throws CameraAccessException;
+
+ /**
+ * Discard all captures currently pending and in-progress as fast as possible.
+ *
+ * <p>The camera device will discard all of its current work as fast as possible. Some in-flight
+ * captures may complete successfully and call {@link CaptureListener#onCaptureCompleted}, while
+ * others will trigger their {@link CaptureListener#onCaptureFailed} callbacks. If a repeating
+ * request or a repeating burst is set, it will be cleared.</p>
+ *
+ * <p>This method is the fastest way to switch the camera device to a new session with
+ * {@link CameraDevice#createCaptureSession}, at the cost of discarding in-progress work. It
+ * must be called before the new session is created. Once all pending requests are either
+ * completed or thrown away, the {@link StateListener#onReady} callback will be called,
+ * if the session has not been closed. Otherwise, the {@link StateListener#onClosed}
+ * callback will be fired when a new session is created by the camera device.</p>
+ *
+ * <p>Cancelling will introduce at least a brief pause in the stream of data from the camera
+ * device, since once the camera device is emptied, the first new request has to make it through
+ * the entire camera pipeline before new output buffers are produced.</p>
+ *
+ * <p>This means that using {@code abortCaptures()} to simply remove pending requests is not
+ * recommended; it's best used for quickly switching output configurations, or for cancelling
+ * long in-progress requests (such as a multi-second capture).</p>
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if this session is no longer active, either because a new
+ * session has been created or the camera device has been closed.
+ *
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ * @see #configureOutputs
+ */
+ public abstract void abortCaptures() throws CameraAccessException;
+
+ /**
+ * Close this capture session asynchronously.
+ *
+ * <p>Closing a session frees up the target output Surfaces of the session for reuse with either a
+ * new session, or to other APIs that can draw to Surfaces.</p>
+ *
+ * <p>Note that creating a new capture session with {@link CameraDevice#createCaptureSession}
+ * will close any existing capture session automatically, and call the older session listener's
+ * {@link StateListener#onClosed} callback. Using {@link CameraDevice#createCaptureSession}
+ * directly without closing is the recommended approach for quickly switching to a new session,
+ * since unchanged target outputs can be reused more efficiently.</p>
+ *
+ * <p>Once a session is closed, all methods on it will throw an IllegalStateException, and any
+ * repeating requests or bursts are stopped (as if {@link #stopRepeating()} was called).
+ * However, any in-progress capture requests submitted to the session will be completed as
+ * normal; once all captures have completed and the session has been torn down,
+ * {@link StateListener#onClosed} will be called.</p>
+ */
+ @Override
+ public abstract void close();
+
+ /**
+ * A listener for tracking the state of a camera capture session.
+ *
+ */
+ public static abstract class StateListener {
+
+ /**
+ * This method is called when the camera device has finished configuring itself, and the
+ * session can start processing capture requests.
+ *
+ * <p>If there are capture requests already queued with the session, they will start
+ * processing once this callback is invoked, and the session will call {@link #onActive}
+ * right after this callback is invoked.</p>
+ *
+ * <p>If no capture requests have been submitted, then the session will invoke
+ * {@link #onReady} right after this callback.</p>
+ *
+ * <p>If the camera device configuration fails, then {@link #onConfigureFailed} will
+ * be invoked instead of this callback.</p>
+ *
+ */
+ public abstract void onConfigured(CameraCaptureSession session);
+
+ /**
+ * This method is called if the session cannot be configured as requested.
+ *
+ * <p>This can happen if the set of requested outputs contains unsupported sizes,
+ * or too many outputs are requested at once.</p>
+ *
+ * <p>The session is considered to be closed, and all methods called on it after this
+ * callback is invoked will throw an IllegalStateException. Any capture requests submitted
+ * to the session prior to this callback will be discarded and will not produce any
+ * callbacks on their listeners.</p>
+ */
+ public abstract void onConfigureFailed(CameraCaptureSession session);
+
+ /**
+ * This method is called every time the session has no more capture requests to process.
+ *
+ * <p>During the creation of a new session, this callback is invoked right after
+ * {@link #onConfigured} if no capture requests were submitted to the session prior to it
+ * completing configuration.</p>
+ *
+ * <p>Otherwise, this callback will be invoked any time the session finishes processing
+ * all of its active capture requests, and no repeating request or burst is set up.</p>
+ *
+ */
+ public void onReady(CameraCaptureSession session) {
+ // default empty implementation
+ }
+
+ /**
+ * This method is called when the session starts actively processing capture requests.
+ *
+ * <p>If capture requests are submitted prior to {@link #onConfigured} being called,
+ * then the session will start processing those requests immediately after the callback,
+ * and this method will be immediately called after {@link #onConfigured}.
+ *
+ * <p>If the session runs out of capture requests to process and calls {@link #onReady},
+ * then this callback will be invoked again once new requests are submitted for capture.</p>
+ */
+ public void onActive(CameraCaptureSession session) {
+ // default empty implementation
+ }
+
+ /**
+ * This method is called when the session is closed.
+ *
+ * <p>A session is closed when a new session is created by the parent camera device,
+ * or when the parent camera device is closed (either by the user closing the device,
+ * or due to a camera device disconnection or fatal error).</p>
+ *
+ * <p>Once a session is closed, all methods on it will throw an IllegalStateException, and
+ * any repeating requests or bursts are stopped (as if {@link #stopRepeating()} was called).
+ * However, any in-progress capture requests submitted to the session will be completed
+ * as normal.</p>
+ */
+ public void onClosed(CameraCaptureSession session) {
+ // default empty implementation
+ }
+ }
+
+ /**
+ * <p>A listener for tracking the progress of a {@link CaptureRequest}
+ * submitted to the camera device.</p>
+ *
+ * <p>This listener is called when a request triggers a capture to start,
+ * and when the capture is complete. In case on an error capturing an image,
+ * the error method is triggered instead of the completion method.</p>
+ *
+ * @see #capture
+ * @see #captureBurst
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ */
+ public static abstract class CaptureListener {
+
+ /**
+ * This constant is used to indicate that no images were captured for
+ * the request.
+ *
+ * @hide
+ */
+ public static final int NO_FRAMES_CAPTURED = -1;
+
+ /**
+ * This method is called when the camera device has started capturing
+ * the output image for the request, at the beginning of image exposure.
+ *
+ * <p>This callback is invoked right as the capture of a frame begins,
+ * so it is the most appropriate time for playing a shutter sound,
+ * or triggering UI indicators of capture.</p>
+ *
+ * <p>The request that is being used for this capture is provided, along
+ * with the actual timestamp for the start of exposure. This timestamp
+ * matches the timestamp that will be included in
+ * {@link CaptureResult#SENSOR_TIMESTAMP the result timestamp field},
+ * and in the buffers sent to each output Surface. These buffer
+ * timestamps are accessible through, for example,
+ * {@link android.media.Image#getTimestamp() Image.getTimestamp()} or
+ * {@link android.graphics.SurfaceTexture#getTimestamp()}.</p>
+ *
+ * <p>For the simplest way to play a shutter sound camera shutter or a
+ * video recording start/stop sound, see the
+ * {@link android.media.MediaActionSound} class.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param camera the CameraDevice sending the callback
+ * @param request the request for the capture that just begun
+ * @param timestamp the timestamp at start of capture, in nanoseconds.
+ *
+ * @see android.media.MediaActionSound
+ */
+ public void onCaptureStarted(CameraDevice camera,
+ CaptureRequest request, long timestamp) {
+ // default empty implementation
+ }
+
+ /**
+ * This method is called when some results from an image capture are
+ * available.
+ *
+ * <p>The result provided here will contain some subset of the fields of
+ * a full result. Multiple onCapturePartial calls may happen per
+ * capture; a given result field will only be present in one partial
+ * capture at most. The final onCaptureCompleted call will always
+ * contain all the fields, whether onCapturePartial was called or
+ * not.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param camera The CameraDevice sending the callback.
+ * @param request The request that was given to the CameraDevice
+ * @param result The partial output metadata from the capture, which
+ * includes a subset of the CaptureResult fields.
+ *
+ * @see #capture
+ * @see #captureBurst
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ *
+ * @hide
+ */
+ public void onCapturePartial(CameraDevice camera,
+ CaptureRequest request, CaptureResult result) {
+ // default empty implementation
+ }
+
+ /**
+ * This method is called when an image capture has completed and the
+ * result metadata is available.
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param camera The CameraDevice sending the callback.
+ * @param request The request that was given to the CameraDevice
+ * @param result The output metadata from the capture, including the
+ * final capture parameters and the state of the camera system during
+ * capture.
+ *
+ * @see #capture
+ * @see #captureBurst
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ */
+ public void onCaptureCompleted(CameraDevice camera,
+ CaptureRequest request, CaptureResult result) {
+ // default empty implementation
+ }
+
+ /**
+ * This method is called instead of {@link #onCaptureCompleted} when the
+ * camera device failed to produce a {@link CaptureResult} for the
+ * request.
+ *
+ * <p>Other requests are unaffected, and some or all image buffers from
+ * the capture may have been pushed to their respective output
+ * streams.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param camera
+ * The CameraDevice sending the callback.
+ * @param request
+ * The request that was given to the CameraDevice
+ * @param failure
+ * The output failure from the capture, including the failure reason
+ * and the frame number.
+ *
+ * @see #capture
+ * @see #captureBurst
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ */
+ public void onCaptureFailed(CameraDevice camera,
+ CaptureRequest request, CaptureFailure failure) {
+ // default empty implementation
+ }
+
+ /**
+ * This method is called independently of the others in CaptureListener,
+ * when a capture sequence finishes and all {@link CaptureResult}
+ * or {@link CaptureFailure} for it have been returned via this listener.
+ *
+ * @param camera
+ * The CameraDevice sending the callback.
+ * @param sequenceId
+ * A sequence ID returned by the {@link #capture} family of functions.
+ * @param lastFrameNumber
+ * The last frame number (returned by {@link CaptureResult#getFrameNumber}
+ * or {@link CaptureFailure#getFrameNumber}) in the capture sequence.
+ * The last frame number may be equal to NO_FRAMES_CAPTURED if no images
+ * were captured for this sequence. This can happen, for example, when a
+ * repeating request or burst is cleared right after being set.
+ *
+ * @see CaptureResult#getFrameNumber()
+ * @see CaptureFailure#getFrameNumber()
+ * @see CaptureResult#getSequenceId()
+ * @see CaptureFailure#getSequenceId()
+ */
+ public void onCaptureSequenceCompleted(CameraDevice camera,
+ int sequenceId, int lastFrameNumber) {
+ // default empty implementation
+ }
+ }
+
+}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 7cc6d1d..c08424a 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -16,7 +16,9 @@
package android.hardware.camera2;
+import android.hardware.camera2.CaptureResult.Key;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.TypeReference;
import android.util.Rational;
import java.util.Collections;
@@ -35,18 +37,109 @@
* @see CameraDevice
* @see CameraManager
*/
-public final class CameraCharacteristics extends CameraMetadata {
+public final class CameraCharacteristics extends CameraMetadata<CameraCharacteristics.Key<?>> {
+
+ /**
+ * A {@code Key} is used to do camera characteristics field lookups with
+ * {@link CameraCharacteristics#get}.
+ *
+ * <p>For example, to get the stream configuration map:
+ * <code><pre>
+ * StreamConfigurationMap map = cameraCharacteristics.get(
+ * CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ * </pre></code>
+ * </p>
+ *
+ * <p>To enumerate over all possible keys for {@link CameraCharacteristics}, see
+ * {@link CameraCharacteristics#getKeys()}.</p>
+ *
+ * @see CameraCharacteristics#get
+ * @see CameraCharacteristics#getKeys()
+ */
+ public static final class Key<T> {
+ private final CameraMetadataNative.Key<T> mKey;
+
+ /**
+ * Visible for testing and vendor extensions only.
+ *
+ * @hide
+ */
+ public Key(String name, Class<T> type) {
+ mKey = new CameraMetadataNative.Key<T>(name, type);
+ }
+
+ /**
+ * Visible for testing and vendor extensions only.
+ *
+ * @hide
+ */
+ public Key(String name, TypeReference<T> typeReference) {
+ mKey = new CameraMetadataNative.Key<T>(name, typeReference);
+ }
+
+ /**
+ * Return a camelCase, period separated name formatted like:
+ * {@code "root.section[.subsections].name"}.
+ *
+ * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
+ * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
+ *
+ * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
+ * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
+ * specific key might look like {@code "com.google.nexus.data.private"}.</p>
+ *
+ * @return String representation of the key name
+ */
+ public String getName() {
+ return mKey.getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final int hashCode() {
+ return mKey.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public final boolean equals(Object o) {
+ return o instanceof Key && ((Key<T>)o).mKey.equals(mKey);
+ }
+
+ /**
+ * Visible for CameraMetadataNative implementation only; do not use.
+ *
+ * TODO: Make this private or remove it altogether.
+ *
+ * @hide
+ */
+ public CameraMetadataNative.Key<T> getNativeKey() {
+ return mKey;
+ }
+
+ @SuppressWarnings({
+ "unused", "unchecked"
+ })
+ private Key(CameraMetadataNative.Key<?> nativeKey) {
+ mKey = (CameraMetadataNative.Key<T>) nativeKey;
+ }
+ }
private final CameraMetadataNative mProperties;
- private List<Key<?>> mAvailableRequestKeys;
- private List<Key<?>> mAvailableResultKeys;
+ private List<CaptureRequest.Key<?>> mAvailableRequestKeys;
+ private List<CaptureResult.Key<?>> mAvailableResultKeys;
/**
* Takes ownership of the passed-in properties object
* @hide
*/
public CameraCharacteristics(CameraMetadataNative properties) {
- mProperties = properties;
+ mProperties = CameraMetadataNative.move(properties);
}
/**
@@ -57,12 +150,55 @@
return new CameraMetadataNative(mProperties);
}
- @Override
+ /**
+ * Get a camera characteristics field value.
+ *
+ * <p>The field definitions can be
+ * found in {@link CameraCharacteristics}.</p>
+ *
+ * <p>Querying the value for the same key more than once will return a value
+ * which is equal to the previous queried value.</p>
+ *
+ * @throws IllegalArgumentException if the key was not valid
+ *
+ * @param key The characteristics field to read.
+ * @return The value of that key, or {@code null} if the field is not set.
+ */
public <T> T get(Key<T> key) {
return mProperties.get(key);
}
/**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected <T> T getProtected(Key<?> key) {
+ return (T) mProperties.get(key);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Class<Key<?>> getKeyClass() {
+ Object thisClass = Key.class;
+ return (Class<Key<?>>)thisClass;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<Key<?>> getKeys() {
+ // Force the javadoc for this function to show up on the CameraCharacteristics page
+ return super.getKeys();
+ }
+
+ /**
* Returns the list of keys supported by this {@link CameraDevice} for querying
* with a {@link CaptureRequest}.
*
@@ -76,9 +212,14 @@
*
* @return List of keys supported by this CameraDevice for CaptureRequests.
*/
- public List<Key<?>> getAvailableCaptureRequestKeys() {
+ @SuppressWarnings({"unchecked"})
+ public List<CaptureRequest.Key<?>> getAvailableCaptureRequestKeys() {
if (mAvailableRequestKeys == null) {
- mAvailableRequestKeys = getAvailableKeyList(CaptureRequest.class);
+ Object crKey = CaptureRequest.Key.class;
+ Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
+
+ mAvailableRequestKeys = Collections.unmodifiableList(
+ getAvailableKeyList(CaptureRequest.class, crKeyTyped));
}
return mAvailableRequestKeys;
}
@@ -97,9 +238,14 @@
*
* @return List of keys supported by this CameraDevice for CaptureResults.
*/
- public List<Key<?>> getAvailableCaptureResultKeys() {
+ @SuppressWarnings({"unchecked"})
+ public List<CaptureResult.Key<?>> getAvailableCaptureResultKeys() {
if (mAvailableResultKeys == null) {
- mAvailableResultKeys = getAvailableKeyList(CaptureResult.class);
+ Object crKey = CaptureResult.Key.class;
+ Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>)crKey;
+
+ mAvailableResultKeys = Collections.unmodifiableList(
+ getAvailableKeyList(CaptureResult.class, crKeyTyped));
}
return mAvailableResultKeys;
}
@@ -113,12 +259,14 @@
* <p>Each key is only listed once in the list. The order of the keys is undefined.</p>
*
* @param metadataClass The subclass of CameraMetadata that you want to get the keys for.
+ * @param keyClass The class of the metadata key, e.g. CaptureRequest.Key.class
*
* @return List of keys supported by this CameraDevice for metadataClass.
*
* @throws IllegalArgumentException if metadataClass is not a subclass of CameraMetadata
*/
- private <T extends CameraMetadata> List<Key<?>> getAvailableKeyList(Class<T> metadataClass) {
+ private <TKey> List<TKey>
+ getAvailableKeyList(Class<?> metadataClass, Class<TKey> keyClass) {
if (metadataClass.equals(CameraMetadata.class)) {
throw new AssertionError(
@@ -128,7 +276,9 @@
"metadataClass must be a subclass of CameraMetadata");
}
- return Collections.unmodifiableList(getKeysStatic(metadataClass, /*instance*/null));
+ List<TKey> staticKeyList = CameraCharacteristics.<TKey>getKeysStatic(
+ metadataClass, keyClass, /*instance*/null);
+ return Collections.unmodifiableList(staticKeyList);
}
/*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
@@ -1046,6 +1196,28 @@
new Key<android.hardware.camera2.params.StreamConfigurationMap>("android.scaler.streamConfigurationMap", android.hardware.camera2.params.StreamConfigurationMap.class);
/**
+ * <p>The crop type that this camera device supports.</p>
+ * <p>When passing a non-centered crop region ({@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}) to a camera
+ * device that only supports CENTER_ONLY cropping, the camera device will move the
+ * crop region to the center of the sensor active array ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize})
+ * and keep the crop region width and height unchanged. The camera device will return the
+ * final used crop region in metadata result {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}.</p>
+ * <p>Camera devices that support FREEFORM cropping will support any crop region that
+ * is inside of the active array. The camera device will apply the same crop region and
+ * return the final used crop region in capture result metadata {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}.</p>
+ * <p>FULL capability devices ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} <code>==</code> FULL) will support
+ * FREEFORM cropping.</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @see CaptureRequest#SCALER_CROP_REGION
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see #SCALER_CROPPING_TYPE_CENTER_ONLY
+ * @see #SCALER_CROPPING_TYPE_FREEFORM
+ */
+ public static final Key<Integer> SCALER_CROPPING_TYPE =
+ new Key<Integer>("android.scaler.croppingType", int.class);
+
+ /**
* <p>Area of raw data which corresponds to only
* active pixels.</p>
* <p>It is smaller or equal to
@@ -1324,19 +1496,6 @@
new Key<Rational[]>("android.sensor.forwardMatrix2", Rational[].class);
/**
- * <p>Gain factor from electrons to raw units when
- * ISO=100</p>
- * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
- * <p><b>Full capability</b> -
- * Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
- * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
- *
- * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
- */
- public static final Key<Rational> SENSOR_BASE_GAIN_FACTOR =
- new Key<Rational>("android.sensor.baseGainFactor", Rational.class);
-
- /**
* <p>A fixed black level offset for each of the color filter arrangement
* (CFA) mosaic channels.</p>
* <p>This tag specifies the zero light value for each of the CFA mosaic
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index ca03dae..77640d1 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -242,10 +242,126 @@
* @see StreamConfigurationMap#getOutputFormats()
* @see StreamConfigurationMap#getOutputSizes(int)
* @see StreamConfigurationMap#getOutputSizes(Class)
+ * @deprecated Use {@link #createCaptureSession} instead
*/
public void configureOutputs(List<Surface> outputs) throws CameraAccessException;
/**
+ * <p>Create a new camera capture session by providing the target output set of Surfaces to the
+ * camera device.</p>
+ *
+ * <p>The active capture session determines the set of potential output Surfaces for
+ * the camera device for each capture request. A given request may use all
+ * or a only some of the outputs. Once the CameraCaptureSession is created, requests can be
+ * can be submitted with {@link CameraCaptureSession#capture capture},
+ * {@link CameraCaptureSession#captureBurst captureBurst},
+ * {@link CameraCaptureSession#setRepeatingRequest setRepeatingRequest}, or
+ * {@link CameraCaptureSession#setRepeatingBurst setRepeatingBurst}.</p>
+ *
+ * <p>Surfaces suitable for inclusion as a camera output can be created for
+ * various use cases and targets:</p>
+ *
+ * <ul>
+ *
+ * <li>For drawing to a {@link android.view.SurfaceView SurfaceView}: Set the size of the
+ * Surface with {@link android.view.SurfaceHolder#setFixedSize} to be one of the sizes
+ * returned by
+ * {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(SurfaceView.class)}
+ * and then obtain the Surface by calling {@link android.view.SurfaceHolder#getSurface}.</li>
+ *
+ * <li>For accessing through an OpenGL texture via a
+ * {@link android.graphics.SurfaceTexture SurfaceTexture}: Set the size of
+ * the SurfaceTexture with
+ * {@link android.graphics.SurfaceTexture#setDefaultBufferSize} to be one
+ * of the sizes returned by
+ * {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(SurfaceTexture.class)}
+ * before creating a Surface from the SurfaceTexture with
+ * {@link Surface#Surface}.</li>
+ *
+ * <li>For recording with {@link android.media.MediaCodec}: Call
+ * {@link android.media.MediaCodec#createInputSurface} after configuring
+ * the media codec to use one of the sizes returned by
+ * {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(MediaCodec.class)}
+ * </li>
+ *
+ * <li>For recording with {@link android.media.MediaRecorder}: Call
+ * {@link android.media.MediaRecorder#getSurface} after configuring the media recorder to use
+ * one of the sizes returned by
+ * {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(MediaRecorder.class)},
+ * or configuring it to use one of the supported
+ * {@link android.media.CamcorderProfile CamcorderProfiles}.</li>
+ *
+ * <li>For efficient YUV processing with {@link android.renderscript}:
+ * Create a RenderScript
+ * {@link android.renderscript.Allocation Allocation} with a supported YUV
+ * type, the IO_INPUT flag, and one of the sizes returned by
+ * {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(Allocation.class)},
+ * Then obtain the Surface with
+ * {@link android.renderscript.Allocation#getSurface}.</li>
+ *
+ * <li>For access to raw, uncompressed or JPEG data in the application: Create a
+ * {@link android.media.ImageReader} object with the one of the supported
+ * {@link StreamConfigurationMap#getOutputFormats() output image formats}, and a
+ * size from the supported
+ * {@link StreamConfigurationMap#getOutputSizes(int) sizes for that format}. Then obtain
+ * a Surface from it with {@link android.media.ImageReader#getSurface}.</li>
+ *
+ * </ul>
+ *
+ * </p>
+ *
+ * <p>The camera device will query each Surface's size and formats upon this
+ * call, so they must be set to a valid setting at this time (in particular:
+ * if the format is user-visible, it must be one of
+ * {@link StreamConfigurationMap#getOutputFormats}; and the size must be one of
+ * {@link StreamConfigurationMap#getOutputSizes(int)}).</p>
+ *
+ * <p>It can take several hundred milliseconds for the session's configuration to complete,
+ * since camera hardware may need to be powered on or reconfigured. Once the configuration is
+ * complete and the session is ready to actually capture data, the provided
+ * {@link CameraCaptureSession.StateListener}'s
+ * {@link CameraCaptureSession.StateListener#onConfigured} callback will be called.</p>
+ *
+ * <p>If a prior CameraCaptureSession already exists when a new one is created, the previous
+ * session is closed. Any in-progress capture requests made on the prior session will be
+ * completed before the new session is configured and is able to start capturing its own
+ * requests. To minimize the transition time, the {@link CameraCaptureSession#abortCaptures}
+ * call can be used to discard the remaining requests for the prior capture session before a new
+ * one is created. Note that once the new session is created, the old one can no longer have its
+ * captures aborted.</p>
+ *
+ * <p>Using larger resolution outputs, or more outputs, can result in slower
+ * output rate from the device.</p>
+ *
+ * <p>Configuring a session with an empty or null list will close the current session, if
+ * any. This can be used to release the current session's target surfaces for another use.</p>
+ *
+ * @param outputs The new set of Surfaces that should be made available as
+ * targets for captured image data.
+ * @param listener The listener to notify about the status of the new capture session.
+ * @param handler The handler on which the listener should be invoked, or {@code null} to use
+ * the current thread's {@link android.os.Looper looper}.
+ * <!--
+ * @return A new camera capture session to use, or null if an empty/null set of Surfaces is
+ * provided.
+ * -->
+ * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
+ * the listener is null, or the handler is null but the current
+ * thread has no looper.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ *
+ * @see CameraCaptureSession
+ * @see StreamConfigurationMap#getOutputFormats()
+ * @see StreamConfigurationMap#getOutputSizes(int)
+ * @see StreamConfigurationMap#getOutputSizes(Class)
+ */
+ public void createCaptureSession(List<Surface> outputs,
+ CameraCaptureSession.StateListener listener, Handler handler)
+ throws CameraAccessException;
+
+ /**
* <p>Create a {@link CaptureRequest.Builder} for new capture requests,
* initialized with template for a target use case. The settings are chosen
* to be the best options for the specific camera device, so it is not
@@ -314,6 +430,7 @@
* @see #captureBurst
* @see #setRepeatingRequest
* @see #setRepeatingBurst
+ * @deprecated Use {@link CameraCaptureSession} instead
*/
public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
throws CameraAccessException;
@@ -358,6 +475,7 @@
* @see #capture
* @see #setRepeatingRequest
* @see #setRepeatingBurst
+ * @deprecated Use {@link CameraCaptureSession} instead
*/
public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Handler handler) throws CameraAccessException;
@@ -416,6 +534,7 @@
* @see #setRepeatingBurst
* @see #stopRepeating
* @see #flush
+ * @deprecated Use {@link CameraCaptureSession} instead
*/
public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Handler handler) throws CameraAccessException;
@@ -474,6 +593,7 @@
* @see #setRepeatingRequest
* @see #stopRepeating
* @see #flush
+ * @deprecated Use {@link CameraCaptureSession} instead
*/
public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
Handler handler) throws CameraAccessException;
@@ -498,6 +618,7 @@
* @see #setRepeatingRequest
* @see #setRepeatingBurst
* @see StateListener#onIdle
+ * @deprecated Use {@link CameraCaptureSession} instead
*/
public void stopRepeating() throws CameraAccessException;
@@ -534,25 +655,24 @@
* @see #setRepeatingRequest
* @see #setRepeatingBurst
* @see #configureOutputs
+ * @deprecated Use {@link CameraCaptureSession} instead
*/
public void flush() throws CameraAccessException;
/**
- * Close the connection to this camera device.
+ * Close the connection to this camera device as quickly as possible.
*
- * <p>After this call, all calls to
- * the camera device interface will throw a {@link IllegalStateException},
- * except for calls to close(). Once the device has fully shut down, the
- * {@link StateListener#onClosed} callback will be called, and the camera is
- * free to be re-opened.</p>
+ * <p>Immediately after this call, all calls to the camera device or active session interface
+ * will throw a {@link IllegalStateException}, except for calls to close(). Once the device has
+ * fully shut down, the {@link StateListener#onClosed} callback will be called, and the camera
+ * is free to be re-opened.</p>
*
- * <p>After this call, besides the final {@link StateListener#onClosed} call, no calls to the
- * device's {@link StateListener} will occur, and any remaining submitted capture requests will
- * not fire their {@link CaptureListener} callbacks.</p>
+ * <p>Immediately after this call, besides the final {@link StateListener#onClosed} calls, no
+ * further callbacks from the device or the active session will occur, and any remaining
+ * submitted capture requests will be discarded, as if
+ * {@link CameraCaptureSession#abortCaptures} had been called, except that no success or failure
+ * callbacks will be invoked.</p>
*
- * <p>To shut down as fast as possible, call the {@link #flush} method and then {@link #close}
- * once the flush completes. This will discard some capture requests, but results in faster
- * shutdown.</p>
*/
@Override
public void close();
@@ -569,6 +689,7 @@
* @see #captureBurst
* @see #setRepeatingRequest
* @see #setRepeatingBurst
+ * @deprecated Use {@link CameraCaptureSession} instead
*/
public static abstract class CaptureListener {
@@ -834,6 +955,7 @@
* <p>The default implementation of this method does nothing.</p>
*
* @param camera the camera device has that become unconfigured
+ * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
*/
public void onUnconfigured(CameraDevice camera) {
// Default empty implementation
@@ -863,6 +985,7 @@
* @see CameraDevice#captureBurst
* @see CameraDevice#setRepeatingBurst
* @see CameraDevice#setRepeatingRequest
+ * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
*/
public void onActive(CameraDevice camera) {
// Default empty implementation
@@ -896,6 +1019,7 @@
*
* @see CameraDevice#configureOutputs
* @see CameraDevice#flush
+ * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
*/
public void onBusy(CameraDevice camera) {
// Default empty implementation
@@ -943,6 +1067,7 @@
* @see CameraDevice#configureOutputs
* @see CameraDevice#stopRepeating
* @see CameraDevice#flush
+ * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
*/
public void onIdle(CameraDevice camera) {
// Default empty implementation
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index cb463a6..accceb1 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -84,9 +84,8 @@
try {
CameraBinderDecorator.throwOnError(
CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor());
- } catch(CameraRuntimeException e) {
- throw new IllegalStateException("Failed to setup camera vendor tags",
- e.asChecked());
+ } catch (CameraRuntimeException e) {
+ handleRecoverableSetupErrors(e, "Failed to set up vendor tags");
}
try {
@@ -199,11 +198,7 @@
}
/**
- * Open a connection to a camera with the given ID. Use
- * {@link #getCameraIdList} to get the list of available camera
- * devices. Note that even if an id is listed, open may fail if the device
- * is disconnected between the calls to {@link #getCameraIdList} and
- * {@link #openCamera}.
+ * Helper for openning a connection to a camera with the given ID.
*
* @param cameraId The unique identifier of the camera device to open
* @param listener The listener for the camera. Must not be null.
@@ -216,20 +211,22 @@
* @throws SecurityException if the application does not have permission to
* access the camera
* @throws IllegalArgumentException if listener or handler is null.
+ * @return A handle to the newly-created camera device.
*
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
- private void openCameraDeviceUserAsync(String cameraId,
+ private CameraDevice openCameraDeviceUserAsync(String cameraId,
CameraDevice.StateListener listener, Handler handler)
throws CameraAccessException {
+ CameraDevice device = null;
try {
synchronized (mLock) {
ICameraDeviceUser cameraUser;
- android.hardware.camera2.impl.CameraDevice device =
+ android.hardware.camera2.impl.CameraDevice deviceImpl =
new android.hardware.camera2.impl.CameraDevice(
cameraId,
listener,
@@ -237,7 +234,7 @@
BinderHolder holder = new BinderHolder();
- ICameraDeviceCallbacks callbacks = device.getCallbacks();
+ ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
int id = Integer.parseInt(cameraId);
try {
mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
@@ -257,7 +254,8 @@
// TODO: factor out listener to be non-nested, then move setter to constructor
// For now, calling setRemoteDevice will fire initial
// onOpened/onUnconfigured callbacks.
- device.setRemoteDevice(cameraUser);
+ deviceImpl.setRemoteDevice(cameraUser);
+ device = deviceImpl;
}
} catch (NumberFormatException e) {
@@ -268,6 +266,7 @@
} catch (RemoteException e) {
// impossible
}
+ return device;
}
/**
@@ -278,20 +277,26 @@
* is disconnected between the calls to {@link #getCameraIdList} and
* {@link #openCamera}.</p>
*
- * <p>If the camera successfully opens after this function call returns,
- * {@link CameraDevice.StateListener#onOpened} will be invoked with the
- * newly opened {@link CameraDevice} in the unconfigured state.</p>
+ * <p>Once the camera is successfully opened, {@link CameraDevice.StateListener#onOpened} will
+ * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
+ * for operation by calling {@link CameraDevice#createCaptureSession} and
+ * {@link CameraDevice#createCaptureRequest}</p>
*
+ * <!--
+ * <p>Since the camera device will be opened asynchronously, any asynchronous operations done
+ * on the returned CameraDevice instance will be queued up until the device startup has
+ * completed and the listener's {@link CameraDevice.StateListener#onOpened onOpened} method is
+ * called. The pending operations are then processed in order.</p>
+ * -->
* <p>If the camera becomes disconnected during initialization
* after this function call returns,
* {@link CameraDevice.StateListener#onDisconnected} with a
* {@link CameraDevice} in the disconnected state (and
* {@link CameraDevice.StateListener#onOpened} will be skipped).</p>
*
- * <p>If the camera fails to initialize after this function call returns,
- * {@link CameraDevice.StateListener#onError} will be invoked with a
- * {@link CameraDevice} in the error state (and
- * {@link CameraDevice.StateListener#onOpened} will be skipped).</p>
+ * <p>If opening the camera device fails, then the device listener's
+ * {@link CameraDevice.StateListener#onError onError} method will be called, and subsequent
+ * calls on the camera device will throw an {@link IllegalStateException}.</p>
*
* @param cameraId
* The unique identifier of the camera device to open
@@ -418,6 +423,18 @@
return mDeviceIdList;
}
+ private void handleRecoverableSetupErrors(CameraRuntimeException e, String msg) {
+ int problem = e.getReason();
+ switch (problem) {
+ case CameraAccessException.CAMERA_DISCONNECTED:
+ String errorMsg = CameraAccessException.getDefaultMessage(problem);
+ Log.w(TAG, msg + ": " + errorMsg);
+ break;
+ default:
+ throw new IllegalStateException(msg, e.asChecked());
+ }
+ }
+
// TODO: this class needs unit tests
// TODO: extract class into top level
private class CameraServiceListener extends ICameraServiceListener.Stub {
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index a11390d..4cde601 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -16,8 +16,7 @@
package android.hardware.camera2;
-import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.utils.TypeReference;
+import android.util.Log;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -36,7 +35,7 @@
*
* <p>
* All instances of CameraMetadata are immutable. The list of keys with {@link #getKeys()}
- * never changes, nor do the values returned by any key with {@link #get} throughout
+ * never changes, nor do the values returned by any key with {@code #get} throughout
* the lifetime of the object.
* </p>
*
@@ -44,7 +43,10 @@
* @see CameraManager
* @see CameraCharacteristics
**/
-public abstract class CameraMetadata {
+public abstract class CameraMetadata<TKey> {
+
+ private static final String TAG = "CameraMetadataAb";
+ private static final boolean VERBOSE = false;
/**
* Set a camera metadata field to a value. The field definitions can be
@@ -74,8 +76,15 @@
*
* @param key The metadata field to read.
* @return The value of that key, or {@code null} if the field is not set.
+ *
+ * @hide
*/
- public abstract <T> T get(Key<T> key);
+ protected abstract <T> T getProtected(TKey key);
+
+ /**
+ * @hide
+ */
+ protected abstract Class<TKey> getKeyClass();
/**
* Returns a list of the keys contained in this map.
@@ -83,14 +92,16 @@
* <p>The list returned is not modifiable, so any attempts to modify it will throw
* a {@code UnsupportedOperationException}.</p>
*
- * <p>All values retrieved by a key from this list with {@link #get} are guaranteed to be
+ * <p>All values retrieved by a key from this list with {@code #get} are guaranteed to be
* non-{@code null}. Each key is only listed once in the list. The order of the keys
* is undefined.</p>
*
* @return List of the keys contained in this map.
*/
- public List<Key<?>> getKeys() {
- return Collections.unmodifiableList(getKeysStatic(this.getClass(), this));
+ @SuppressWarnings("unchecked")
+ public List<TKey> getKeys() {
+ Class<CameraMetadata<TKey>> thisClass = (Class<CameraMetadata<TKey>>) getClass();
+ return Collections.unmodifiableList(getKeysStatic(thisClass, getKeyClass(), this));
}
/**
@@ -101,24 +112,31 @@
* Optionally, if {@code instance} is not null, then filter out any keys with null values.
* </p>
*/
- /*package*/ static ArrayList<Key<?>> getKeysStatic(Class<? extends CameraMetadata> type,
- CameraMetadata instance) {
- ArrayList<Key<?>> keyList = new ArrayList<Key<?>>();
+ /*package*/ @SuppressWarnings("unchecked")
+ static <TKey> ArrayList<TKey> getKeysStatic(
+ Class<?> type, Class<TKey> keyClass,
+ CameraMetadata<TKey> instance) {
+
+ if (VERBOSE) Log.v(TAG, "getKeysStatic for " + type);
+
+ ArrayList<TKey> keyList = new ArrayList<TKey>();
Field[] fields = type.getDeclaredFields();
for (Field field : fields) {
// Filter for Keys that are public
- if (field.getType().isAssignableFrom(Key.class) &&
+ if (field.getType().isAssignableFrom(keyClass) &&
(field.getModifiers() & Modifier.PUBLIC) != 0) {
- Key<?> key;
+
+ TKey key;
try {
- key = (Key<?>) field.get(instance);
+ key = (TKey) field.get(instance);
} catch (IllegalAccessException e) {
throw new AssertionError("Can't get IllegalAccessException", e);
} catch (IllegalArgumentException e) {
throw new AssertionError("Can't get IllegalArgumentException", e);
}
- if (instance == null || instance.get(key) != null) {
+
+ if (instance == null || instance.getProtected(key) != null) {
keyList.add(key);
}
}
@@ -127,113 +145,6 @@
return keyList;
}
- // TODO: make final or abstract
- public static class Key<T> {
-
- private boolean mHasTag;
- private int mTag;
- private final Class<T> mType;
- private final TypeReference<T> mTypeReference;
- private final String mName;
-
- /**
- * @hide
- */
- public Key(String name, Class<T> type) {
- if (name == null) {
- throw new NullPointerException("Key needs a valid name");
- } else if (type == null) {
- throw new NullPointerException("Type needs to be non-null");
- }
- mName = name;
- mType = type;
- mTypeReference = TypeReference.createSpecializedTypeReference(type);
- }
-
- /**
- * @hide
- */
- @SuppressWarnings("unchecked")
- public Key(String name, TypeReference<T> typeReference) {
- if (name == null) {
- throw new NullPointerException("Key needs a valid name");
- } else if (typeReference == null) {
- throw new NullPointerException("TypeReference needs to be non-null");
- }
- mName = name;
- mType = (Class<T>)typeReference.getRawType();
- mTypeReference = typeReference;
- }
-
- public final String getName() {
- return mName;
- }
-
- @Override
- public final int hashCode() {
- return mName.hashCode() ^ mTypeReference.hashCode();
- }
-
- @Override
- public final boolean equals(Object o) {
- if (this == o) {
- return true;
- }
-
- if (!(o instanceof Key)) {
- return false;
- }
-
- Key<?> lhs = (Key<?>)o;
- return mName.equals(lhs.mName) && mTypeReference.equals(lhs.mTypeReference);
- }
-
- /**
- * <p>
- * Get the tag corresponding to this key. This enables insertion into the
- * native metadata.
- * </p>
- *
- * <p>This value is looked up the first time, and cached subsequently.</p>
- *
- * @return The tag numeric value corresponding to the string
- *
- * @hide
- */
- public final int getTag() {
- if (!mHasTag) {
- mTag = CameraMetadataNative.getTag(mName);
- mHasTag = true;
- }
- return mTag;
- }
-
- /**
- * Get the raw class backing the type {@code T} for this key.
- *
- * <p>The distinction is only important if {@code T} is a generic, e.g.
- * {@code Range<Integer>} since the nested type will be erased.</p>
- *
- * @hide
- */
- public final Class<T> getType() {
- // TODO: remove this; other places should use #getTypeReference() instead
- return mType;
- }
-
- /**
- * Get the type reference backing the type {@code T} for this key.
- *
- * <p>The distinction is only important if {@code T} is a generic, e.g.
- * {@code Range<Integer>} since the nested type will be retained.</p>
- *
- * @hide
- */
- public final TypeReference<T> getTypeReference() {
- return mTypeReference;
- }
- }
-
/*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* The enum values below this point are generated from metadata
* definitions in /system/media/camera/docs. Do not modify by hand or
@@ -336,7 +247,6 @@
* <li>Manual sensitivity control<ul>
* <li>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}</li>
* <li>{@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange}</li>
- * <li>{@link CameraCharacteristics#SENSOR_BASE_GAIN_FACTOR android.sensor.baseGainFactor}</li>
* </ul>
* </li>
* <li>Manual lens control<ul>
@@ -357,7 +267,6 @@
* result.</p>
*
* @see CaptureRequest#BLACK_LEVEL_LOCK
- * @see CameraCharacteristics#SENSOR_BASE_GAIN_FACTOR
* @see CaptureRequest#SENSOR_EXPOSURE_TIME
* @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE
* @see CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE
@@ -446,6 +355,22 @@
public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 5;
//
+ // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
+ //
+
+ /**
+ * <p>The camera device will only support centered crop regions.</p>
+ * @see CameraCharacteristics#SCALER_CROPPING_TYPE
+ */
+ public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0;
+
+ /**
+ * <p>The camera device will support arbitrarily chosen crop regions.</p>
+ * @see CameraCharacteristics#SCALER_CROPPING_TYPE
+ */
+ public static final int SCALER_CROPPING_TYPE_FREEFORM = 1;
+
+ //
// Enumeration values for CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
//
@@ -1365,8 +1290,7 @@
/**
* <p>If the flash is available and charged, fire flash
- * for this capture based on android.flash.firingPower and
- * android.flash.firingTime.</p>
+ * for this capture.</p>
* @see CaptureRequest#FLASH_MODE
*/
public static final int FLASH_MODE_SINGLE = 1;
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 54ffd6b..a4aa296 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -16,7 +16,9 @@
package android.hardware.camera2;
+import android.hardware.camera2.CameraCharacteristics.Key;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.TypeReference;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Rational;
@@ -25,6 +27,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Objects;
@@ -58,7 +61,98 @@
* @see CameraDevice#setRepeatingRequest
* @see CameraDevice#createCaptureRequest
*/
-public final class CaptureRequest extends CameraMetadata implements Parcelable {
+public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
+ implements Parcelable {
+
+ /**
+ * A {@code Key} is used to do capture request field lookups with
+ * {@link CaptureResult#get} or to set fields with
+ * {@link CaptureRequest.Builder#set(Key, Object)}.
+ *
+ * <p>For example, to set the crop rectangle for the next capture:
+ * <code><pre>
+ * Rect cropRectangle = new Rect(0, 0, 640, 480);
+ * captureRequestBuilder.set(SCALER_CROP_REGION, cropRectangle);
+ * </pre></code>
+ * </p>
+ *
+ * <p>To enumerate over all possible keys for {@link CaptureResult}, see
+ * {@link CameraCharacteristics#getAvailableCaptureResultKeys}.</p>
+ *
+ * @see CaptureResult#get
+ * @see CameraCharacteristics#getAvailableCaptureResultKeys
+ */
+ public final static class Key<T> {
+ private final CameraMetadataNative.Key<T> mKey;
+
+ /**
+ * Visible for testing and vendor extensions only.
+ *
+ * @hide
+ */
+ public Key(String name, Class<T> type) {
+ mKey = new CameraMetadataNative.Key<T>(name, type);
+ }
+
+ /**
+ * Visible for testing and vendor extensions only.
+ *
+ * @hide
+ */
+ public Key(String name, TypeReference<T> typeReference) {
+ mKey = new CameraMetadataNative.Key<T>(name, typeReference);
+ }
+
+ /**
+ * Return a camelCase, period separated name formatted like:
+ * {@code "root.section[.subsections].name"}.
+ *
+ * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
+ * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
+ *
+ * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
+ * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
+ * specific key might look like {@code "com.google.nexus.data.private"}.</p>
+ *
+ * @return String representation of the key name
+ */
+ public String getName() {
+ return mKey.getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final int hashCode() {
+ return mKey.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public final boolean equals(Object o) {
+ return o instanceof Key && ((Key<T>)o).mKey.equals(mKey);
+ }
+
+ /**
+ * Visible for CameraMetadataNative implementation only; do not use.
+ *
+ * TODO: Make this private or remove it altogether.
+ *
+ * @hide
+ */
+ public CameraMetadataNative.Key<T> getNativeKey() {
+ return mKey;
+ }
+
+ @SuppressWarnings({ "unchecked" })
+ /*package*/ Key(CameraMetadataNative.Key<?> nativeKey) {
+ mKey = (CameraMetadataNative.Key<T>) nativeKey;
+ }
+ }
private final HashSet<Surface> mSurfaceSet;
private final CameraMetadataNative mSettings;
@@ -93,17 +187,58 @@
* Used by the Builder to create a mutable CaptureRequest.
*/
private CaptureRequest(CameraMetadataNative settings) {
- mSettings = settings;
+ mSettings = CameraMetadataNative.move(settings);
mSurfaceSet = new HashSet<Surface>();
}
- @SuppressWarnings("unchecked")
- @Override
+ /**
+ * Get a capture request field value.
+ *
+ * <p>The field definitions can be found in {@link CaptureRequest}.</p>
+ *
+ * <p>Querying the value for the same key more than once will return a value
+ * which is equal to the previous queried value.</p>
+ *
+ * @throws IllegalArgumentException if the key was not valid
+ *
+ * @param key The result field to read.
+ * @return The value of that key, or {@code null} if the field is not set.
+ */
public <T> T get(Key<T> key) {
return mSettings.get(key);
}
/**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected <T> T getProtected(Key<?> key) {
+ return (T) mSettings.get(key);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Class<Key<?>> getKeyClass() {
+ Object thisClass = Key.class;
+ return (Class<Key<?>>)thisClass;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<Key<?>> getKeys() {
+ // Force the javadoc for this function to show up on the CaptureRequest page
+ return super.getKeys();
+ }
+
+ /**
* Retrieve the tag for this request, if any.
*
* <p>This tag is not used for anything by the camera device, but can be
@@ -569,9 +704,9 @@
* should be nonnegative.</p>
* <p>If all regions have 0 weight, then no specific metering area
* needs to be used by the camera device. If the metering region is
- * outside the current {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, the camera device
- * will ignore the sections outside the region and output the
- * used sections in the frame metadata.</p>
+ * outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
+ * the camera device will ignore the sections outside the region and output the
+ * used sections in the result metadata.</p>
*
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
@@ -644,9 +779,9 @@
* should be nonnegative.</p>
* <p>If all regions have 0 weight, then no specific focus area
* needs to be used by the camera device. If the focusing region is
- * outside the current {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, the camera device
- * will ignore the sections outside the region and output the
- * used sections in the frame metadata.</p>
+ * outside the the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture
+ * result metadata, the camera device will ignore the sections outside
+ * the region and output the used sections in the result metadata.</p>
*
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
@@ -731,9 +866,9 @@
* should be nonnegative.</p>
* <p>If all regions have 0 weight, then no specific auto-white balance (AWB) area
* needs to be used by the camera device. If the AWB region is
- * outside the current {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, the camera device
- * will ignore the sections outside the region and output the
- * used sections in the frame metadata.</p>
+ * outside the the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
+ * the camera device will ignore the sections outside the region and output the
+ * used sections in the result metadata.</p>
*
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
@@ -1126,8 +1261,11 @@
* output, cropping to a smaller region if necessary to
* maintain the stream's aspect ratio.</p>
* <p>HAL2.x uses only (x, y, width)</p>
- * <p>Any additional per-stream cropping must be done to
- * maximize the final pixel area of the stream.</p>
+ * <p>The crop region is applied after the RAW to other color space (e.g. YUV)
+ * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage,
+ * it is not croppable. The crop region will be ignored by raw streams.</p>
+ * <p>For non-raw streams, any additional per-stream cropping will
+ * be done to maximize the final pixel area of the stream.</p>
* <p>For example, if the crop region is set to a 4:3 aspect
* ratio, then 4:3 streams should use the exact crop
* region. 16:9 streams should further crop vertically
@@ -1308,8 +1446,16 @@
* camera device. Applications can request lens shading map data by setting
* {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} to ON, and then the camera device will provide
* lens shading map data in {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap}, with size specified
- * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}.</p>
+ * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}; the returned shading map data will be the one
+ * applied by the camera device for this capture request.</p>
+ * <p>The shading map data may depend on the AE and AWB statistics, therefore the reliability
+ * of the map data may be affected by the AE and AWB algorithms. When AE and AWB are in
+ * AUTO modes({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF and {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} <code>!=</code> OFF),
+ * to get best results, it is recommended that the applications wait for the AE and AWB to
+ * be converged before using the returned shading map data.</p>
*
+ * @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureRequest#CONTROL_AWB_MODE
* @see CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE
* @see CaptureResult#STATISTICS_LENS_SHADING_MAP
* @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
@@ -1458,7 +1604,7 @@
* {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}, and {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}.
* These values are always available, and as close as possible to the
* actually used nonlinear/nonglobal transforms.</p>
- * <p>If a request is sent with TRANSFORM_MATRIX with the camera device's
+ * <p>If a request is sent with CONTRAST_CURVE with the camera device's
* provided curve in FAST or HIGH_QUALITY, the image's tonemap will be
* roughly the same.</p>
*
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index f91fcb9..61d491b 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -17,9 +17,12 @@
package android.hardware.camera2;
import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.params.Face;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.Log;
import android.util.Rational;
+import java.util.List;
+
/**
* <p>The results of a single image capture from the image sensor.</p>
*
@@ -36,7 +39,98 @@
* <p>{@link CameraCharacteristics} objects are immutable.</p>
*
*/
-public final class CaptureResult extends CameraMetadata {
+public final class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
+
+ private static final String TAG = "CaptureResult";
+ private static final boolean VERBOSE = false;
+
+ /**
+ * A {@code Key} is used to do capture result field lookups with
+ * {@link CaptureResult#get}.
+ *
+ * <p>For example, to get the timestamp corresponding to the exposure of the first row:
+ * <code><pre>
+ * long timestamp = captureResult.get(CaptureResult.SENSOR_TIMESTAMP);
+ * </pre></code>
+ * </p>
+ *
+ * <p>To enumerate over all possible keys for {@link CaptureResult}, see
+ * {@link CameraCharacteristics#getAvailableCaptureResultKeys}.</p>
+ *
+ * @see CaptureResult#get
+ * @see CameraCharacteristics#getAvailableCaptureResultKeys
+ */
+ public final static class Key<T> {
+ private final CameraMetadataNative.Key<T> mKey;
+
+ /**
+ * Visible for testing and vendor extensions only.
+ *
+ * @hide
+ */
+ public Key(String name, Class<T> type) {
+ mKey = new CameraMetadataNative.Key<T>(name, type);
+ }
+
+ /**
+ * Visible for testing and vendor extensions only.
+ *
+ * @hide
+ */
+ public Key(String name, TypeReference<T> typeReference) {
+ mKey = new CameraMetadataNative.Key<T>(name, typeReference);
+ }
+
+ /**
+ * Return a camelCase, period separated name formatted like:
+ * {@code "root.section[.subsections].name"}.
+ *
+ * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
+ * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
+ *
+ * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
+ * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
+ * specific key might look like {@code "com.google.nexus.data.private"}.</p>
+ *
+ * @return String representation of the key name
+ */
+ public String getName() {
+ return mKey.getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final int hashCode() {
+ return mKey.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public final boolean equals(Object o) {
+ return o instanceof Key && ((Key<T>)o).mKey.equals(mKey);
+ }
+
+ /**
+ * Visible for CameraMetadataNative implementation only; do not use.
+ *
+ * TODO: Make this private or remove it altogether.
+ *
+ * @hide
+ */
+ public CameraMetadataNative.Key<T> getNativeKey() {
+ return mKey;
+ }
+
+ @SuppressWarnings({ "unchecked" })
+ /*package*/ Key(CameraMetadataNative.Key<?> nativeKey) {
+ mKey = (CameraMetadataNative.Key<T>) nativeKey;
+ }
+ }
private final CameraMetadataNative mResults;
private final CaptureRequest mRequest;
@@ -55,7 +149,10 @@
throw new IllegalArgumentException("parent was null");
}
- mResults = results;
+ mResults = CameraMetadataNative.move(results);
+ if (mResults.isEmpty()) {
+ throw new AssertionError("Results must not be empty");
+ }
mRequest = parent;
mSequenceId = sequenceId;
}
@@ -68,9 +165,85 @@
return new CameraMetadataNative(mResults);
}
- @Override
+ /**
+ * Creates a request-less result.
+ *
+ * <p><strong>For testing only.</strong></p>
+ * @hide
+ */
+ public CaptureResult(CameraMetadataNative results, int sequenceId) {
+ if (results == null) {
+ throw new IllegalArgumentException("results was null");
+ }
+
+ mResults = CameraMetadataNative.move(results);
+ if (mResults.isEmpty()) {
+ throw new AssertionError("Results must not be empty");
+ }
+
+ mRequest = null;
+ mSequenceId = sequenceId;
+ }
+
+ /**
+ * Get a capture result field value.
+ *
+ * <p>The field definitions can be found in {@link CaptureResult}.</p>
+ *
+ * <p>Querying the value for the same key more than once will return a value
+ * which is equal to the previous queried value.</p>
+ *
+ * @throws IllegalArgumentException if the key was not valid
+ *
+ * @param key The result field to read.
+ * @return The value of that key, or {@code null} if the field is not set.
+ */
public <T> T get(Key<T> key) {
- return mResults.get(key);
+ T value = mResults.get(key);
+ if (VERBOSE) Log.v(TAG, "#get for Key = " + key.getName() + ", returned value = " + value);
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected <T> T getProtected(Key<?> key) {
+ return (T) mResults.get(key);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Class<Key<?>> getKeyClass() {
+ Object thisClass = Key.class;
+ return (Class<Key<?>>)thisClass;
+ }
+
+ /**
+ * Dumps the native metadata contents to logcat.
+ *
+ * <p>Visibility for testing/debugging only. The results will not
+ * include any synthesized keys, as they are invisible to the native layer.</p>
+ *
+ * @hide
+ */
+ public void dumpToLog() {
+ mResults.dumpToLog();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<Key<?>> getKeys() {
+ // Force the javadoc for this function to show up on the CaptureResult page
+ return super.getKeys();
}
/**
@@ -110,6 +283,7 @@
* @return int frame number
*/
public int getFrameNumber() {
+ // TODO: @hide REQUEST_FRAME_COUNT
return get(REQUEST_FRAME_COUNT);
}
@@ -369,9 +543,9 @@
* should be nonnegative.</p>
* <p>If all regions have 0 weight, then no specific metering area
* needs to be used by the camera device. If the metering region is
- * outside the current {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, the camera device
- * will ignore the sections outside the region and output the
- * used sections in the frame metadata.</p>
+ * outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
+ * the camera device will ignore the sections outside the region and output the
+ * used sections in the result metadata.</p>
*
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
@@ -642,9 +816,9 @@
* should be nonnegative.</p>
* <p>If all regions have 0 weight, then no specific focus area
* needs to be used by the camera device. If the focusing region is
- * outside the current {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, the camera device
- * will ignore the sections outside the region and output the
- * used sections in the frame metadata.</p>
+ * outside the the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture
+ * result metadata, the camera device will ignore the sections outside
+ * the region and output the used sections in the result metadata.</p>
*
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
@@ -1126,9 +1300,9 @@
* should be nonnegative.</p>
* <p>If all regions have 0 weight, then no specific auto-white balance (AWB) area
* needs to be used by the camera device. If the AWB region is
- * outside the current {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, the camera device
- * will ignore the sections outside the region and output the
- * used sections in the frame metadata.</p>
+ * outside the the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
+ * the camera device will ignore the sections outside the region and output the
+ * used sections in the result metadata.</p>
*
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
@@ -1749,8 +1923,11 @@
* output, cropping to a smaller region if necessary to
* maintain the stream's aspect ratio.</p>
* <p>HAL2.x uses only (x, y, width)</p>
- * <p>Any additional per-stream cropping must be done to
- * maximize the final pixel area of the stream.</p>
+ * <p>The crop region is applied after the RAW to other color space (e.g. YUV)
+ * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage,
+ * it is not croppable. The crop region will be ignored by raw streams.</p>
+ * <p>For non-raw streams, any additional per-stream cropping will
+ * be done to maximize the final pixel area of the stream.</p>
* <p>For example, if the crop region is set to a 4:3 aspect
* ratio, then 4:3 streams should use the exact crop
* region. 16:9 streams should further crop vertically
@@ -1885,21 +2062,6 @@
new Key<Long>("android.sensor.timestamp", long.class);
/**
- * <p>The temperature of the sensor, sampled at the time
- * exposure began for this frame.</p>
- * <p>The thermal diode being queried should be inside the sensor PCB, or
- * somewhere close to it.</p>
- * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
- * <p><b>Full capability</b> -
- * Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
- * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
- *
- * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
- */
- public static final Key<Float> SENSOR_TEMPERATURE =
- new Key<Float>("android.sensor.temperature", float.class);
-
- /**
* <p>The estimated camera neutral color in the native sensor colorspace at
* the time of capture.</p>
* <p>This value gives the neutral color point encoded as an RGB value in the
@@ -2006,8 +2168,16 @@
* camera device. Applications can request lens shading map data by setting
* {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} to ON, and then the camera device will provide
* lens shading map data in {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap}, with size specified
- * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}.</p>
+ * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}; the returned shading map data will be the one
+ * applied by the camera device for this capture request.</p>
+ * <p>The shading map data may depend on the AE and AWB statistics, therefore the reliability
+ * of the map data may be affected by the AE and AWB algorithms. When AE and AWB are in
+ * AUTO modes({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF and {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} <code>!=</code> OFF),
+ * to get best results, it is recommended that the applications wait for the AE and AWB to
+ * be converged before using the returned shading map data.</p>
*
+ * @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureRequest#CONTROL_AWB_MODE
* @see CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE
* @see CaptureResult#STATISTICS_LENS_SHADING_MAP
* @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
@@ -2338,7 +2508,7 @@
* {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}, and {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}.
* These values are always available, and as close as possible to the
* actually used nonlinear/nonglobal transforms.</p>
- * <p>If a request is sent with TRANSFORM_MATRIX with the camera device's
+ * <p>If a request is sent with CONTRAST_CURVE with the camera device's
* provided curve in FAST or HIGH_QUALITY, the image's tonemap will be
* roughly the same.</p>
*
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
new file mode 100644
index 0000000..54568ed
--- /dev/null
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -0,0 +1,368 @@
+/*
+ * 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.hardware.camera2;
+
+import android.graphics.Bitmap;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.location.Location;
+import android.media.ExifInterface;
+import android.media.Image;
+import android.util.Size;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * The {@link DngCreator} class provides functions to write raw pixel data as a DNG file.
+ *
+ * <p>
+ * This class is designed to be used with the {@link android.graphics.ImageFormat#RAW_SENSOR}
+ * buffers available from {@link android.hardware.camera2.CameraDevice}, or with Bayer-type raw
+ * pixel data that is otherwise generated by an application. The DNG metadata tags will be
+ * generated from a {@link android.hardware.camera2.CaptureResult} object or set directly.
+ * </p>
+ *
+ * <p>
+ * The DNG file format is a cross-platform file format that is used to store pixel data from
+ * camera sensors with minimal pre-processing applied. DNG files allow for pixel data to be
+ * defined in a user-defined colorspace, and have associated metadata that allow for this
+ * pixel data to be converted to the standard CIE XYZ colorspace during post-processing.
+ * </p>
+ *
+ * <p>
+ * For more information on the DNG file format and associated metadata, please refer to the
+ * <a href=
+ * "https://wwwimages2.adobe.com/content/dam/Adobe/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf">
+ * Adobe DNG 1.4.0.0 specification</a>.
+ * </p>
+ */
+public final class DngCreator implements AutoCloseable {
+
+ /**
+ * Create a new DNG object.
+ *
+ * <p>
+ * It is not necessary to call any set methods to write a well-formatted DNG file.
+ * </p>
+ * <p>
+ * DNG metadata tags will be generated from the corresponding parameters in the
+ * {@link android.hardware.camera2.CaptureResult} object. This removes or overrides
+ * all previous tags set.
+ * </p>
+ *
+ * @param characteristics an object containing the static
+ * {@link android.hardware.camera2.CameraCharacteristics}.
+ * @param metadata a metadata object to generate tags from.
+ */
+ public DngCreator(CameraCharacteristics characteristics, CaptureResult metadata) {
+ if (characteristics == null || metadata == null) {
+ throw new NullPointerException("Null argument to DngCreator constructor");
+ }
+ nativeInit(characteristics.getNativeCopy(), metadata.getNativeCopy());
+ }
+
+ /**
+ * Set the orientation value to write.
+ *
+ * <p>
+ * This will be written as the TIFF "Orientation" tag {@code (0x0112)}.
+ * Calling this will override any prior settings for this tag.
+ * </p>
+ *
+ * @param orientation the orientation value to set, one of:
+ * <ul>
+ * <li>{@link android.media.ExifInterface#ORIENTATION_NORMAL}</li>
+ * <li>{@link android.media.ExifInterface#ORIENTATION_FLIP_HORIZONTAL}</li>
+ * <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_180}</li>
+ * <li>{@link android.media.ExifInterface#ORIENTATION_FLIP_VERTICAL}</li>
+ * <li>{@link android.media.ExifInterface#ORIENTATION_TRANSPOSE}</li>
+ * <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_90}</li>
+ * <li>{@link android.media.ExifInterface#ORIENTATION_TRANSVERSE}</li>
+ * <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_270}</li>
+ * </ul>
+ * @return this {@link #DngCreator} object.
+ */
+ public DngCreator setOrientation(int orientation) {
+
+ if (orientation < ExifInterface.ORIENTATION_UNDEFINED ||
+ orientation > ExifInterface.ORIENTATION_ROTATE_270) {
+ throw new IllegalArgumentException("Orientation " + orientation +
+ " is not a valid EXIF orientation value");
+ }
+ nativeSetOrientation(orientation);
+ return this;
+ }
+
+ /**
+ * Set the thumbnail image.
+ *
+ * <p>
+ * Pixel data will be converted to a Baseline TIFF RGB image, with 8 bits per color channel.
+ * The alpha channel will be discarded.
+ * </p>
+ *
+ * <p>
+ * The given bitmap should not be altered while this object is in use.
+ * </p>
+ *
+ * @param pixels a {@link android.graphics.Bitmap} of pixel data.
+ * @return this {@link #DngCreator} object.
+ */
+ public DngCreator setThumbnail(Bitmap pixels) {
+ if (pixels == null) {
+ throw new NullPointerException("Null argument to setThumbnail");
+ }
+
+ Bitmap.Config config = pixels.getConfig();
+
+ if (config != Bitmap.Config.ARGB_8888) {
+ pixels = pixels.copy(Bitmap.Config.ARGB_8888, false);
+ if (pixels == null) {
+ throw new IllegalArgumentException("Unsupported Bitmap format " + config);
+ }
+ nativeSetThumbnailBitmap(pixels);
+ }
+
+ return this;
+ }
+
+ /**
+ * Set the thumbnail image.
+ *
+ * <p>
+ * Pixel data is interpreted as a {@link android.graphics.ImageFormat#YUV_420_888} image.
+ * </p>
+ *
+ * <p>
+ * The given image should not be altered while this object is in use.
+ * </p>
+ *
+ * @param pixels an {@link android.media.Image} object with the format
+ * {@link android.graphics.ImageFormat#YUV_420_888}.
+ * @return this {@link #DngCreator} object.
+ */
+ public DngCreator setThumbnail(Image pixels) {
+ if (pixels == null) {
+ throw new NullPointerException("Null argument to setThumbnail");
+ }
+
+ int format = pixels.getFormat();
+ if (format != ImageFormat.YUV_420_888) {
+ throw new IllegalArgumentException("Unsupported image format " + format);
+ }
+
+ Image.Plane[] planes = pixels.getPlanes();
+ nativeSetThumbnailImage(pixels.getWidth(), pixels.getHeight(), planes[0].getBuffer(),
+ planes[0].getRowStride(), planes[0].getPixelStride(), planes[1].getBuffer(),
+ planes[1].getRowStride(), planes[1].getPixelStride(), planes[1].getBuffer(),
+ planes[1].getRowStride(), planes[1].getPixelStride());
+
+ return this;
+ }
+
+
+ /**
+ * Set image location metadata.
+ *
+ * <p>
+ * The given location object must contain at least a valid time, latitude, and longitude
+ * (equivalent to the values returned by {@link android.location.Location#getTime()},
+ * {@link android.location.Location#getLatitude()}, and
+ * {@link android.location.Location#getLongitude()} methods).
+ * </p>
+ *
+ * @param location an {@link android.location.Location} object to set.
+ * @return this {@link #DngCreator} object.
+ *
+ * @throws java.lang.IllegalArgumentException if the given location object doesn't
+ * contain enough information to set location metadata.
+ */
+ public DngCreator setLocation(Location location) {
+ /*TODO*/
+ return this;
+ }
+
+ /**
+ * Set the user description string to write.
+ *
+ * <p>
+ * This is equivalent to setting the TIFF "ImageDescription" tag {@code (0x010E)}.
+ * </p>
+ *
+ * @param description the user description string.
+ * @return this {@link #DngCreator} object.
+ */
+ public DngCreator setDescription(String description) {
+ /*TODO*/
+ return this;
+ }
+
+ /**
+ * Write the {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data to a DNG file with
+ * the currently configured metadata.
+ *
+ * <p>
+ * Raw pixel data must have 16 bits per pixel, and the input must contain at least
+ * {@code offset + 2 * width * height)} bytes. The width and height of
+ * the input are taken from the width and height set in the {@link DngCreator} metadata tags,
+ * and will typically be equal to the width and height of
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
+ * The pixel layout in the input is determined from the reported color filter arrangement (CFA)
+ * set in {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT}. If insufficient
+ * metadata is available to write a well-formatted DNG file, an
+ * {@link java.lang.IllegalStateException} will be thrown.
+ * </p>
+ *
+ * @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
+ * @param size the {@link Size} of the image to write, in pixels.
+ * @param pixels an {@link java.io.InputStream} of pixel data to write.
+ * @param offset the offset of the raw image in bytes. This indicates how many bytes will
+ * be skipped in the input before any pixel data is read.
+ *
+ * @throws IOException if an error was encountered in the input or output stream.
+ * @throws java.lang.IllegalStateException if not enough metadata information has been
+ * set to write a well-formatted DNG file.
+ * @throws java.lang.IllegalArgumentException if the size passed in does not match the
+ */
+ public void writeInputStream(OutputStream dngOutput, Size size, InputStream pixels, long offset)
+ throws IOException {
+ if (dngOutput == null || pixels == null) {
+ throw new NullPointerException("Null argument to writeImage");
+ }
+ nativeWriteInputStream(dngOutput, pixels, offset);
+ }
+
+ /**
+ * Write the {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data to a DNG file with
+ * the currently configured metadata.
+ *
+ * <p>
+ * Raw pixel data must have 16 bits per pixel, and the input must contain at least
+ * {@code offset + 2 * width * height)} bytes. The width and height of
+ * the input are taken from the width and height set in the {@link DngCreator} metadata tags,
+ * and will typically be equal to the width and height of
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
+ * The pixel layout in the input is determined from the reported color filter arrangement (CFA)
+ * set in {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT}. If insufficient
+ * metadata is available to write a well-formatted DNG file, an
+ * {@link java.lang.IllegalStateException} will be thrown.
+ * </p>
+ *
+ * @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
+ * @param size the {@link Size} of the image to write, in pixels.
+ * @param pixels an {@link java.nio.ByteBuffer} of pixel data to write.
+ * @param offset the offset of the raw image in bytes. This indicates how many bytes will
+ * be skipped in the input before any pixel data is read.
+ *
+ * @throws IOException if an error was encountered in the input or output stream.
+ * @throws java.lang.IllegalStateException if not enough metadata information has been
+ * set to write a well-formatted DNG file.
+ */
+ public void writeByteBuffer(OutputStream dngOutput, Size size, ByteBuffer pixels, long offset)
+ throws IOException {
+ if (dngOutput == null || pixels == null) {
+ throw new NullPointerException("Null argument to writeImage");
+ }
+ nativeWriteByteBuffer(dngOutput, pixels, offset);
+ }
+
+ /**
+ * Write the pixel data to a DNG file with the currently configured metadata.
+ *
+ * <p>
+ * For this method to succeed, the {@link android.media.Image} input must contain
+ * {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data, otherwise an
+ * {@link java.lang.IllegalArgumentException} will be thrown.
+ * </p>
+ *
+ * @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
+ * @param pixels an {@link android.media.Image} to write.
+ *
+ * @throws java.io.IOException if an error was encountered in the output stream.
+ * @throws java.lang.IllegalArgumentException if an image with an unsupported format was used.
+ * @throws java.lang.IllegalStateException if not enough metadata information has been
+ * set to write a well-formatted DNG file.
+ */
+ public void writeImage(OutputStream dngOutput, Image pixels) throws IOException {
+ if (dngOutput == null || pixels == null) {
+ throw new NullPointerException("Null argument to writeImage");
+ }
+
+ int format = pixels.getFormat();
+ if (format != ImageFormat.RAW_SENSOR) {
+ throw new IllegalArgumentException("Unsupported image format " + format);
+ }
+
+ Image.Plane[] planes = pixels.getPlanes();
+ nativeWriteImage(dngOutput, pixels.getWidth(), pixels.getHeight(), planes[0].getBuffer(),
+ planes[0].getRowStride(), planes[0].getPixelStride());
+ }
+
+ @Override
+ public void close() {
+ nativeDestroy();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * This field is used by native code, do not access or modify.
+ */
+ private long mNativeContext;
+
+ private static native void nativeClassInit();
+
+ private synchronized native void nativeInit(CameraMetadataNative nativeCharacteristics,
+ CameraMetadataNative nativeResult);
+
+ private synchronized native void nativeDestroy();
+
+ private synchronized native void nativeSetOrientation(int orientation);
+
+ private synchronized native void nativeSetThumbnailBitmap(Bitmap bitmap);
+
+ private synchronized native void nativeSetThumbnailImage(int width, int height,
+ ByteBuffer yBuffer, int yRowStride,
+ int yPixStride, ByteBuffer uBuffer,
+ int uRowStride, int uPixStride,
+ ByteBuffer vBuffer, int vRowStride,
+ int vPixStride);
+
+ private synchronized native void nativeWriteImage(OutputStream out, int width, int height,
+ ByteBuffer rawBuffer, int rowStride,
+ int pixStride) throws IOException;
+
+ private synchronized native void nativeWriteByteBuffer(OutputStream out, ByteBuffer rawBuffer,
+ long offset) throws IOException;
+
+ private synchronized native void nativeWriteInputStream(OutputStream out, InputStream rawStream,
+ long offset) throws IOException;
+
+ static {
+ nativeClassInit();
+ }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index e78ffff..b082a70 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -19,6 +19,7 @@
import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.ICameraDeviceCallbacks;
@@ -253,6 +254,13 @@
}
@Override
+ public void createCaptureSession(List<Surface> outputs,
+ CameraCaptureSession.StateListener listener, Handler handler)
+ throws CameraAccessException {
+ // TODO
+ }
+
+ @Override
public CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException {
synchronized (mLock) {
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 27cfd38..ab2c49a 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -20,7 +20,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.marshal.Marshaler;
import android.hardware.camera2.marshal.MarshalQueryable;
@@ -46,10 +46,14 @@
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.params.StreamConfigurationDuration;
import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.utils.TypeReference;
import android.os.Parcelable;
import android.os.Parcel;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
@@ -58,7 +62,147 @@
* Implementation of camera metadata marshal/unmarshal across Binder to
* the camera service
*/
-public class CameraMetadataNative extends CameraMetadata implements Parcelable {
+public class CameraMetadataNative implements Parcelable {
+
+ public static class Key<T> {
+ private boolean mHasTag;
+ private int mTag;
+ private final Class<T> mType;
+ private final TypeReference<T> mTypeReference;
+ private final String mName;
+
+ /**
+ * Visible for testing only.
+ *
+ * <p>Use the CameraCharacteristics.Key, CaptureResult.Key, or CaptureRequest.Key
+ * for application code or vendor-extended keys.</p>
+ */
+ public Key(String name, Class<T> type) {
+ if (name == null) {
+ throw new NullPointerException("Key needs a valid name");
+ } else if (type == null) {
+ throw new NullPointerException("Type needs to be non-null");
+ }
+ mName = name;
+ mType = type;
+ mTypeReference = TypeReference.createSpecializedTypeReference(type);
+ }
+
+ /**
+ * Visible for testing only.
+ *
+ * <p>Use the CameraCharacteristics.Key, CaptureResult.Key, or CaptureRequest.Key
+ * for application code or vendor-extended keys.</p>
+ */
+ @SuppressWarnings("unchecked")
+ public Key(String name, TypeReference<T> typeReference) {
+ if (name == null) {
+ throw new NullPointerException("Key needs a valid name");
+ } else if (typeReference == null) {
+ throw new NullPointerException("TypeReference needs to be non-null");
+ }
+ mName = name;
+ mType = (Class<T>)typeReference.getRawType();
+ mTypeReference = typeReference;
+ }
+
+ /**
+ * Return a camelCase, period separated name formatted like:
+ * {@code "root.section[.subsections].name"}.
+ *
+ * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
+ * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
+ *
+ * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
+ * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
+ * specific key might look like {@code "com.google.nexus.data.private"}.</p>
+ *
+ * @return String representation of the key name
+ */
+ public final String getName() {
+ return mName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final int hashCode() {
+ return mName.hashCode() ^ mTypeReference.hashCode();
+ }
+
+ /**
+ * Compare this key against other native keys, request keys, result keys, and
+ * characteristics keys.
+ *
+ * <p>Two keys are considered equal if their name and type reference are equal.</p>
+ *
+ * <p>Note that the equality against non-native keys is one-way. A native key may be equal
+ * to a result key; but that same result key will not be equal to a native key.</p>
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ public final boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ Key<?> lhs;
+
+ if (o instanceof CaptureResult.Key) {
+ lhs = ((CaptureResult.Key)o).getNativeKey();
+ } else if (o instanceof CaptureRequest.Key) {
+ lhs = ((CaptureRequest.Key)o).getNativeKey();
+ } else if (o instanceof CameraCharacteristics.Key) {
+ lhs = ((CameraCharacteristics.Key)o).getNativeKey();
+ } else if ((o instanceof Key)) {
+ lhs = (Key<?>)o;
+ } else {
+ return false;
+ }
+
+ return mName.equals(lhs.mName) && mTypeReference.equals(lhs.mTypeReference);
+ }
+
+ /**
+ * <p>
+ * Get the tag corresponding to this key. This enables insertion into the
+ * native metadata.
+ * </p>
+ *
+ * <p>This value is looked up the first time, and cached subsequently.</p>
+ *
+ * @return The tag numeric value corresponding to the string
+ */
+ public final int getTag() {
+ if (!mHasTag) {
+ mTag = CameraMetadataNative.getTag(mName);
+ mHasTag = true;
+ }
+ return mTag;
+ }
+
+ /**
+ * Get the raw class backing the type {@code T} for this key.
+ *
+ * <p>The distinction is only important if {@code T} is a generic, e.g.
+ * {@code Range<Integer>} since the nested type will be erased.</p>
+ */
+ public final Class<T> getType() {
+ // TODO: remove this; other places should use #getTypeReference() instead
+ return mType;
+ }
+
+ /**
+ * Get the type reference backing the type {@code T} for this key.
+ *
+ * <p>The distinction is only important if {@code T} is a generic, e.g.
+ * {@code Range<Integer>} since the nested type will be retained.</p>
+ */
+ public final TypeReference<T> getTypeReference() {
+ return mTypeReference;
+ }
+ }
private static final String TAG = "CameraMetadataJV";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -84,6 +228,20 @@
}
}
+ /**
+ * Move the contents from {@code other} into a new camera metadata instance.</p>
+ *
+ * <p>After this call, {@code other} will become empty.</p>
+ *
+ * @param other the previous metadata instance which will get pilfered
+ * @return a new metadata instance with the values from {@code other} moved into it
+ */
+ public static CameraMetadataNative move(CameraMetadataNative other) {
+ CameraMetadataNative newObject = new CameraMetadataNative();
+ newObject.swap(other);
+ return newObject;
+ }
+
public static final Parcelable.Creator<CameraMetadataNative> CREATOR =
new Parcelable.Creator<CameraMetadataNative>() {
@Override
@@ -109,8 +267,36 @@
nativeWriteToParcel(dest);
}
- @Override
+ /**
+ * @hide
+ */
+ public <T> T get(CameraCharacteristics.Key<T> key) {
+ return get(key.getNativeKey());
+ }
+
+ /**
+ * @hide
+ */
+ public <T> T get(CaptureResult.Key<T> key) {
+ return get(key.getNativeKey());
+ }
+
+ /**
+ * @hide
+ */
+ public <T> T get(CaptureRequest.Key<T> key) {
+ return get(key.getNativeKey());
+ }
+
+ /**
+ * Look-up a metadata field value by its key.
+ *
+ * @param key a non-{@code null} key instance
+ * @return the field corresponding to the {@code key}, or {@code null} if no value was set
+ */
public <T> T get(Key<T> key) {
+ Preconditions.checkNotNull(key, "key must not be null");
+
T value = getOverride(key);
if (value != null) {
return value;
@@ -152,6 +338,18 @@
setBase(key, value);
}
+ public <T> void set(CaptureRequest.Key<T> key, T value) {
+ set(key.getNativeKey(), value);
+ }
+
+ public <T> void set(CaptureResult.Key<T> key, T value) {
+ set(key.getNativeKey(), value);
+ }
+
+ public <T> void set(CameraCharacteristics.Key<T> key, T value) {
+ set(key.getNativeKey(), value);
+ }
+
// Keep up-to-date with camera_metadata.h
/**
* @hide
@@ -188,6 +386,18 @@
mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final
}
+ private <T> T getBase(CameraCharacteristics.Key<T> key) {
+ return getBase(key.getNativeKey());
+ }
+
+ private <T> T getBase(CaptureResult.Key<T> key) {
+ return getBase(key.getNativeKey());
+ }
+
+ private <T> T getBase(CaptureRequest.Key<T> key) {
+ return getBase(key.getNativeKey());
+ }
+
private <T> T getBase(Key<T> key) {
int tag = key.getTag();
byte[] values = readValues(tag);
@@ -342,6 +552,18 @@
return new StreamConfigurationMap(configurations, minFrameDurations, stallDurations);
}
+ private <T> void setBase(CameraCharacteristics.Key<T> key, T value) {
+ setBase(key.getNativeKey(), value);
+ }
+
+ private <T> void setBase(CaptureResult.Key<T> key, T value) {
+ setBase(key.getNativeKey(), value);
+ }
+
+ private <T> void setBase(CaptureRequest.Key<T> key, T value) {
+ setBase(key.getNativeKey(), value);
+ }
+
private <T> void setBase(Key<T> key, T value) {
int tag = key.getTag();
@@ -440,6 +662,7 @@
private native synchronized byte[] nativeReadValues(int tag);
private native synchronized void nativeWriteValues(int tag, byte[] src);
+ private native synchronized void nativeDump() throws IOException; // dump to ALOGD
private static native int nativeGetTagFromKey(String keyName)
throws IllegalArgumentException;
@@ -531,6 +754,22 @@
return nativeReadValues(tag);
}
+ /**
+ * Dumps the native metadata contents to logcat.
+ *
+ * <p>Visibility for testing/debugging only. The results will not
+ * include any synthesized keys, as they are invisible to the native layer.</p>
+ *
+ * @hide
+ */
+ public void dumpToLog() {
+ try {
+ nativeDump();
+ } catch (IOException e) {
+ Log.wtf(TAG, "Dump logging failed", e);
+ }
+ }
+
@Override
protected void finalize() throws Throwable {
try {
@@ -599,5 +838,4 @@
nativeClassInit();
registerAllMarshalers();
}
-
}
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java
index ddaf870..a8aa376 100644
--- a/core/java/android/hardware/hdmi/HdmiCecMessage.java
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java
@@ -123,6 +123,7 @@
* @param p HdmiCecMessage object to read the Rating from
* @return a new HdmiCecMessage created from the data in the parcel
*/
+ @Override
public HdmiCecMessage createFromParcel(Parcel p) {
int source = p.readInt();
int destination = p.readInt();
@@ -131,6 +132,7 @@
p.readByteArray(params);
return new HdmiCecMessage(source, destination, opcode, params);
}
+ @Override
public HdmiCecMessage[] newArray(int size) {
return new HdmiCecMessage[size];
}
@@ -139,11 +141,40 @@
@Override
public String toString() {
StringBuffer s = new StringBuffer();
- s.append(String.format("src: %d dst: %d op: %2X params: ", mSource, mDestination, mOpcode));
- for (byte data : mParams) {
- s.append(String.format("%02X ", data));
+ s.append(String.format("<%s> src: %d, dst: %d",
+ opcodeToString(mOpcode), mSource, mDestination));
+ if (mParams.length > 0) {
+ s.append(", params:");
+ for (byte data : mParams) {
+ s.append(String.format(" %02X", data));
+ }
}
return s.toString();
}
+
+ private static String opcodeToString(int opcode) {
+ switch (opcode) {
+ case HdmiCec.MESSAGE_FEATURE_ABORT:
+ return "Feature Abort";
+ case HdmiCec.MESSAGE_CEC_VERSION:
+ return "CEC Version";
+ case HdmiCec.MESSAGE_REQUEST_ARC_INITIATION:
+ return "Request ARC Initiation";
+ case HdmiCec.MESSAGE_REQUEST_ARC_TERMINATION:
+ return "Request ARC Termination";
+ case HdmiCec.MESSAGE_REPORT_ARC_INITIATED:
+ return "Report ARC Initiated";
+ case HdmiCec.MESSAGE_REPORT_ARC_TERMINATED:
+ return "Report ARC Terminated";
+ case HdmiCec.MESSAGE_TEXT_VIEW_ON:
+ return "Text View On";
+ case HdmiCec.MESSAGE_ACTIVE_SOURCE:
+ return "Active Source";
+ case HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS:
+ return "Give Device Power Status";
+ default:
+ return String.format("Opcode: %02X", opcode);
+ }
+ }
}
diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index 70cc708..5df4baf 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -47,7 +47,7 @@
}
/**
- * Get Ethernet configuration
+ * Get Ethernet configuration.
* @return the Ethernet Configuration, contained in {@link IpConfiguration}.
*/
public IpConfiguration getConfiguration() {
@@ -61,8 +61,7 @@
}
/**
- * Set Ethernet configuration
- * @return true if setting success
+ * Set Ethernet configuration.
*/
public void setConfiguration(IpConfiguration config) {
try {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index af45fa0..d3c6fd5 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2443,8 +2443,10 @@
pw.print(prefix); pw.print(" Capacity: ");
printmAh(pw, helper.getPowerProfile().getBatteryCapacity());
pw.print(", Computed drain: "); printmAh(pw, helper.getComputedPower());
- pw.print(", Min drain: "); printmAh(pw, helper.getMinDrainedPower());
- pw.print(", Max drain: "); printmAh(pw, helper.getMaxDrainedPower());
+ pw.print(", actual drain: "); printmAh(pw, helper.getMinDrainedPower());
+ if (helper.getMinDrainedPower() != helper.getMaxDrainedPower()) {
+ pw.print("-"); printmAh(pw, helper.getMaxDrainedPower());
+ }
pw.println();
for (int i=0; i<sippers.size(); i++) {
BatterySipper bs = sippers.get(i);
@@ -3351,7 +3353,10 @@
}
hprinter.printNextItem(pw, rec, baseTime, checkin,
(flags&DUMP_VERBOSE) != 0);
- } else if (rec.eventCode != HistoryItem.EVENT_NONE) {
+ } else if (false && rec.eventCode != HistoryItem.EVENT_NONE) {
+ // This is an attempt to aggregate the previous state and generate
+ // fake events to reflect that state at the point where we start
+ // printing real events. It doesn't really work right, so is turned off.
if (tracker == null) {
tracker = new HistoryEventTracker();
}
diff --git a/core/java/android/os/CommonBundle.java b/core/java/android/os/CommonBundle.java
index e11f170..c1b202c 100644
--- a/core/java/android/os/CommonBundle.java
+++ b/core/java/android/os/CommonBundle.java
@@ -18,11 +18,10 @@
import android.util.ArrayMap;
import android.util.Log;
-import android.util.SparseArray;
import java.io.Serializable;
import java.util.ArrayList;
-import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -304,6 +303,16 @@
}
/**
+ * Inserts all mappings from the given Map into this CommonBundle.
+ *
+ * @param map a Map
+ */
+ void putAll(Map map) {
+ unparcel();
+ mMap.putAll(map);
+ }
+
+ /**
* Returns a Set containing the Strings used as keys in this Bundle.
*
* @return a Set of String keys
diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java
new file mode 100644
index 0000000..7f8bc9f
--- /dev/null
+++ b/core/java/android/os/FileBridge.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.SOCK_STREAM;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import libcore.io.IoBridge;
+import libcore.io.IoUtils;
+import libcore.io.Memory;
+import libcore.io.Streams;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.SyncFailedException;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+/**
+ * Simple bridge that allows file access across process boundaries without
+ * returning the underlying {@link FileDescriptor}. This is useful when the
+ * server side needs to strongly assert that a client side is completely
+ * hands-off.
+ *
+ * @hide
+ */
+public class FileBridge extends Thread {
+ private static final String TAG = "FileBridge";
+
+ // TODO: consider extending to support bidirectional IO
+
+ private static final int MSG_LENGTH = 8;
+
+ /** CMD_WRITE [len] [data] */
+ private static final int CMD_WRITE = 1;
+ /** CMD_FSYNC */
+ private static final int CMD_FSYNC = 2;
+
+ private FileDescriptor mTarget;
+
+ private final FileDescriptor mServer = new FileDescriptor();
+ private final FileDescriptor mClient = new FileDescriptor();
+
+ private volatile boolean mClosed;
+
+ public FileBridge() {
+ try {
+ Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
+ } catch (ErrnoException e) {
+ throw new RuntimeException("Failed to create bridge");
+ }
+ }
+
+ public boolean isClosed() {
+ return mClosed;
+ }
+
+ public void setTargetFile(FileDescriptor target) {
+ mTarget = target;
+ }
+
+ public FileDescriptor getClientSocket() {
+ return mClient;
+ }
+
+ @Override
+ public void run() {
+ final byte[] temp = new byte[8192];
+ try {
+ while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
+ final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
+
+ if (cmd == CMD_WRITE) {
+ // Shuttle data into local file
+ int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
+ while (len > 0) {
+ int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
+ IoBridge.write(mTarget, temp, 0, n);
+ len -= n;
+ }
+
+ } else if (cmd == CMD_FSYNC) {
+ // Sync and echo back to confirm
+ Os.fsync(mTarget);
+ IoBridge.write(mServer, temp, 0, MSG_LENGTH);
+ }
+ }
+
+ // Client was closed; one last fsync
+ Os.fsync(mTarget);
+
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Failed during bridge: ", e);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed during bridge: ", e);
+ } finally {
+ IoUtils.closeQuietly(mTarget);
+ IoUtils.closeQuietly(mServer);
+ IoUtils.closeQuietly(mClient);
+ mClosed = true;
+ }
+ }
+
+ public static class FileBridgeOutputStream extends OutputStream {
+ private final FileDescriptor mClient;
+ private final byte[] mTemp = new byte[MSG_LENGTH];
+
+ public FileBridgeOutputStream(FileDescriptor client) {
+ mClient = client;
+ }
+
+ @Override
+ public void close() throws IOException {
+ IoBridge.closeAndSignalBlockedThreads(mClient);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ Memory.pokeInt(mTemp, 0, CMD_FSYNC, ByteOrder.BIG_ENDIAN);
+ IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
+
+ // Wait for server to ack
+ if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) {
+ if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == CMD_FSYNC) {
+ return;
+ }
+ }
+
+ throw new SyncFailedException("Failed to fsync() across bridge");
+ }
+
+ @Override
+ public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+ Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount);
+ Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN);
+ Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN);
+ IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
+ IoBridge.write(mClient, buffer, byteOffset, byteCount);
+ }
+
+ @Override
+ public void write(int oneByte) throws IOException {
+ Streams.writeSingleByte(this, oneByte);
+ }
+ }
+}
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index c2cd3be..cd8d515 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -17,7 +17,14 @@
package android.os;
import android.util.ArrayMap;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
import java.util.Set;
/**
@@ -25,7 +32,8 @@
* restored.
*
*/
-public final class PersistableBundle extends CommonBundle {
+public final class PersistableBundle extends CommonBundle implements XmlUtils.WriteMapCallback {
+ private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
public static final PersistableBundle EMPTY;
static final Parcel EMPTY_PARCEL;
@@ -88,6 +96,38 @@
}
/**
+ * Constructs a PersistableBundle containing the mappings passed in.
+ *
+ * @param map a Map containing only those items that can be persisted.
+ * @throws IllegalArgumentException if any element of #map cannot be persisted.
+ */
+ private PersistableBundle(Map<String, Object> map) {
+ super();
+
+ // First stuff everything in.
+ putAll(map);
+
+ // Now verify each item throwing an exception if there is a violation.
+ Set<String> keys = map.keySet();
+ Iterator<String> iterator = keys.iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ Object value = map.get(key);
+ if (value instanceof Map) {
+ // Fix up any Maps by replacing them with PersistableBundles.
+ putPersistableBundle(key, new PersistableBundle((Map<String, Object>) value));
+ } else if (!(value instanceof Integer) && !(value instanceof Long) &&
+ !(value instanceof Double) && !(value instanceof String) &&
+ !(value instanceof int[]) && !(value instanceof long[]) &&
+ !(value instanceof double[]) && !(value instanceof String[]) &&
+ !(value instanceof PersistableBundle) && (value != null)) {
+ throw new IllegalArgumentException("Bad value in PersistableBundle key=" + key +
+ " value=" + value);
+ }
+ }
+ }
+
+ /**
* Make a PersistableBundle for a single key/value pair.
*
* @hide
@@ -206,6 +246,7 @@
*
* @param bundle a PersistableBundle
*/
+ @Override
public void putAll(PersistableBundle bundle) {
super.putAll(bundle);
}
@@ -323,6 +364,7 @@
* @param key a String, or null
* @param value a Bundle object, or null
*/
+ @Override
public void putPersistableBundle(String key, PersistableBundle value) {
super.putPersistableBundle(key, value);
}
@@ -539,6 +581,57 @@
super.readFromParcelInner(parcel);
}
+ /** @hide */
+ @Override
+ public void writeUnknownObject(Object v, String name, XmlSerializer out)
+ throws XmlPullParserException, IOException {
+ if (v instanceof PersistableBundle) {
+ out.startTag(null, TAG_PERSISTABLEMAP);
+ out.attribute(null, "name", name);
+ ((PersistableBundle) v).saveToXml(out);
+ out.endTag(null, TAG_PERSISTABLEMAP);
+ } else {
+ throw new XmlPullParserException("Unknown Object o=" + v);
+ }
+ }
+
+ /** @hide */
+ public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+ unparcel();
+ XmlUtils.writeMapXml(mMap, out, this);
+ }
+
+ /** @hide */
+ static class MyReadMapCallback implements XmlUtils.ReadMapCallback {
+ @Override
+ public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
+ throws XmlPullParserException, IOException {
+ if (TAG_PERSISTABLEMAP.equals(tag)) {
+ return restoreFromXml(in);
+ }
+ throw new XmlPullParserException("Unknown tag=" + tag);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
+ XmlPullParserException {
+ final int outerDepth = in.getDepth();
+ final String startTag = in.getName();
+ final String[] tagName = new String[1];
+ int event;
+ while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+ (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
+ if (event == XmlPullParser.START_TAG) {
+ return new PersistableBundle((Map<String, Object>)
+ XmlUtils.readThisMapXml(in, startTag, tagName, new MyReadMapCallback()));
+ }
+ }
+ return EMPTY;
+ }
+
@Override
synchronized public String toString() {
if (mParcelledData != null) {
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index d2d6ade..5e005d0 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -37,20 +37,22 @@
* @hide
*/
public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callback {
+ private static final String TAG = "SeekBarVolumizer";
public interface Callback {
void onSampleStarting(SeekBarVolumizer sbv);
}
- private Context mContext;
- private Handler mHandler;
+ private final Context mContext;
+ private final Handler mHandler;
private final Callback mCallback;
+ private final Uri mDefaultUri;
+ private final AudioManager mAudioManager;
+ private final int mStreamType;
+ private final int mMaxStreamVolume;
- private AudioManager mAudioManager;
- private int mStreamType;
private int mOriginalStreamVolume;
private Ringtone mRingtone;
-
private int mLastProgress = -1;
private SeekBar mSeekBar;
private int mVolumeBeforeMute = -1;
@@ -58,9 +60,10 @@
private static final int MSG_SET_STREAM_VOLUME = 0;
private static final int MSG_START_SAMPLE = 1;
private static final int MSG_STOP_SAMPLE = 2;
+ private static final int MSG_INIT_SAMPLE = 3;
private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
- private ContentObserver mVolumeObserver = new ContentObserver(mHandler) {
+ private ContentObserver mVolumeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
@@ -71,27 +74,17 @@
}
};
- public SeekBarVolumizer(Context context, SeekBar seekBar, int streamType, Uri defaultUri,
+ public SeekBarVolumizer(Context context, int streamType, Uri defaultUri,
Callback callback) {
mContext = context;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mStreamType = streamType;
- mSeekBar = seekBar;
-
- HandlerThread thread = new HandlerThread(VolumePreference.TAG + ".CallbackHandler");
+ mMaxStreamVolume = mAudioManager.getStreamMaxVolume(mStreamType);
+ HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
thread.start();
mHandler = new Handler(thread.getLooper(), this);
mCallback = callback;
-
- initSeekBar(seekBar, defaultUri);
- }
-
- private void initSeekBar(SeekBar seekBar, Uri defaultUri) {
- seekBar.setMax(mAudioManager.getStreamMaxVolume(mStreamType));
mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
- seekBar.setProgress(mOriginalStreamVolume);
- seekBar.setOnSeekBarChangeListener(this);
-
mContext.getContentResolver().registerContentObserver(
System.getUriFor(System.VOLUME_SETTINGS[mStreamType]),
false, mVolumeObserver);
@@ -105,12 +98,16 @@
defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI;
}
}
+ mDefaultUri = defaultUri;
+ mHandler.sendEmptyMessage(MSG_INIT_SAMPLE);
+ }
- mRingtone = RingtoneManager.getRingtone(mContext, defaultUri);
-
- if (mRingtone != null) {
- mRingtone.setStreamType(mStreamType);
- }
+ public void setSeekBar(SeekBar seekBar) {
+ mSeekBar = seekBar;
+ mSeekBar.setOnSeekBarChangeListener(null);
+ mSeekBar.setMax(mMaxStreamVolume);
+ mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume);
+ mSeekBar.setOnSeekBarChangeListener(this);
}
@Override
@@ -125,12 +122,22 @@
case MSG_STOP_SAMPLE:
onStopSample();
break;
+ case MSG_INIT_SAMPLE:
+ onInitSample();
+ break;
default:
- Log.e(VolumePreference.TAG, "invalid SeekBarVolumizer message: "+msg.what);
+ Log.e(TAG, "invalid SeekBarVolumizer message: "+msg.what);
}
return true;
}
+ private void onInitSample() {
+ mRingtone = RingtoneManager.getRingtone(mContext, mDefaultUri);
+ if (mRingtone != null) {
+ mRingtone.setStreamType(mStreamType);
+ }
+ }
+
private void postStartSample() {
mHandler.removeMessages(MSG_START_SAMPLE);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE),
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index 171e5c3..df9e10e 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -66,7 +66,8 @@
super.onBindDialogView(view);
final SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
- mSeekBarVolumizer = new SeekBarVolumizer(getContext(), seekBar, mStreamType, null, this);
+ mSeekBarVolumizer = new SeekBarVolumizer(getContext(), mStreamType, null, this);
+ mSeekBarVolumizer.setSeekBar(seekBar);
getPreferenceManager().registerOnActivityStopListener(this);
diff --git a/core/java/android/provider/TvContract.java b/core/java/android/provider/TvContract.java
index 5ffffb5..e4f93a8 100644
--- a/core/java/android/provider/TvContract.java
+++ b/core/java/android/provider/TvContract.java
@@ -462,7 +462,7 @@
* <p>
* A value of 1 indicates the channel is included in the channel list that applications use
* to browse channels, a value of 0 indicates the channel is not included in the list. If
- * not specified, this value is set to 1 by default.
+ * not specified, this value is set to 1 (browsable) by default.
* </p><p>
* Type: INTEGER (boolean)
* </p>
@@ -470,6 +470,36 @@
public static final String COLUMN_BROWSABLE = "browsable";
/**
+ * The flag indicating whether this TV channel is searchable or not.
+ * <p>
+ * In some regions, it is not allowed to surface search results for a given channel without
+ * broadcaster's consent. This is used to impose such restriction. A value of 1 indicates
+ * the channel is searchable and can be included in search results, a value of 0 indicates
+ * the channel and its TV programs are hidden from search. If not specified, this value is
+ * set to 1 (searchable) by default.
+ * </p>
+ * <p>
+ * Type: INTEGER (boolean)
+ * </p>
+ */
+ public static final String COLUMN_SEARCHABLE = "searchable";
+
+ /**
+ * The flag indicating whether this TV channel is locked or not.
+ * <p>
+ * This is primarily used for alternative parental control to prevent unauthorized users
+ * from watching the current channel regardless of the content rating. A value of 1
+ * indicates the channel is locked and the user is required to enter passcode to unlock it
+ * in order to watch the current program from the channel, a value of 0 indicates the
+ * channel is not locked thus the user is not prompted to enter passcode If not specified,
+ * this value is set to 0 (not locked) by default.
+ * </p><p>
+ * Type: INTEGER (boolean)
+ * </p>
+ */
+ public static final String COLUMN_LOCKED = "locked";
+
+ /**
* Generic data used by individual TV input services.
* <p>
* Type: BLOB
@@ -544,6 +574,33 @@
public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
/**
+ * The comma-separated genre string of this TV program.
+ * <p>
+ * Use the same language appeared in the underlying broadcast standard, if applicable. (For
+ * example, one can refer to the genre strings used in Genre Descriptor of ATSC A/65 or
+ * Content Descriptor of ETSI EN 300 468, if appropriate.) Otherwise, use one of the
+ * following genres:
+ * <ul>
+ * <li>Family/Kids</li>
+ * <li>Sports</li>
+ * <li>Shopping</li>
+ * <li>Movies</li>
+ * <li>Comedy</li>
+ * <li>Travel</li>
+ * <li>Drama</li>
+ * <li>Education</li>
+ * <li>Animal/Wildlife</li>
+ * <li>News</li>
+ * <li>Gaming</li>
+ * <li>Others</li>
+ * </ul>
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_GENRE = "genre";
+
+ /**
* The description of this TV program that is displayed to the user by default.
* <p>
* The maximum length of this field is 256 characters.
@@ -566,6 +623,17 @@
public static final String COLUMN_LONG_DESCRIPTION = "long_description";
/**
+ * The comma-separated audio languages of this TV program.
+ * <p>
+ * This is used to describe available audio languages included in the program. Use
+ * 3-character language code as specified by ISO 639-2.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_AUDIO_LANGUAGE = "audio_language";
+
+ /**
* Generic data used by TV input services.
* <p>
* Type: BLOB
diff --git a/core/java/android/tv/ITvInputHardware.aidl b/core/java/android/tv/ITvInputHardware.aidl
new file mode 100644
index 0000000..7250453
--- /dev/null
+++ b/core/java/android/tv/ITvInputHardware.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.tv;
+
+import android.tv.TvStreamConfig;
+import android.view.KeyEvent;
+import android.view.Surface;
+
+/**
+ * TvInputService representing a physical port should connect to HAL through this interface.
+ * Framework will take care of communication among system services including TvInputManagerService,
+ * HdmiControlService, AudioService, etc.
+ *
+ * @hide
+ */
+interface ITvInputHardware {
+ /**
+ * Make the input render on the surface according to the config. In case of HDMI, this will
+ * trigger CEC commands for adjusting active HDMI source. Returns true on success.
+ */
+ boolean setSurface(in Surface surface, in TvStreamConfig config);
+ /**
+ * Set volume for this stream via AudioGain. (TBD)
+ */
+ void setVolume(float volume);
+
+ /**
+ * Dispatch key event to HDMI service. The events would be automatically converted to
+ * HDMI CEC commands. If the hardware is not representing an HDMI port, this method will fail.
+ */
+ boolean dispatchKeyEventToHdmi(in KeyEvent event);
+}
diff --git a/core/java/android/tv/ITvInputHardwareCallback.aidl b/core/java/android/tv/ITvInputHardwareCallback.aidl
new file mode 100644
index 0000000..83041be
--- /dev/null
+++ b/core/java/android/tv/ITvInputHardwareCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.tv;
+
+import android.tv.TvStreamConfig;
+
+/**
+ * @hide
+ */
+oneway interface ITvInputHardwareCallback {
+ void onReleased();
+ void onStreamConfigChanged(in TvStreamConfig[] configs);
+}
diff --git a/core/java/android/tv/ITvInputManager.aidl b/core/java/android/tv/ITvInputManager.aidl
index b756aba..c6f8d79 100644
--- a/core/java/android/tv/ITvInputManager.aidl
+++ b/core/java/android/tv/ITvInputManager.aidl
@@ -19,7 +19,10 @@
import android.content.ComponentName;
import android.graphics.Rect;
import android.net.Uri;
+import android.tv.ITvInputHardware;
+import android.tv.ITvInputHardwareCallback;
import android.tv.ITvInputClient;
+import android.tv.TvInputHardwareInfo;
import android.tv.TvInputInfo;
import android.view.Surface;
@@ -46,4 +49,10 @@
int userId);
void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId);
void removeOverlayView(in IBinder sessionToken, int userId);
+
+ // For TV input hardware binding
+ List<TvInputHardwareInfo> getHardwareList();
+ ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
+ int userId);
+ void releaseTvInputHardware(int deviceId, in ITvInputHardware hardware, int userId);
}
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/core/java/android/tv/TvInputHardwareInfo.aidl
new file mode 100644
index 0000000..484ab60
--- /dev/null
+++ b/core/java/android/tv/TvInputHardwareInfo.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.tv;
+
+parcelable TvInputHardwareInfo;
diff --git a/core/java/android/tv/TvInputHardwareInfo.java b/core/java/android/tv/TvInputHardwareInfo.java
new file mode 100644
index 0000000..b0dc58e
--- /dev/null
+++ b/core/java/android/tv/TvInputHardwareInfo.java
@@ -0,0 +1,93 @@
+/*
+ * 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.tv;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Simple container for information about TV input hardware.
+ * Not for third-party developers.
+ *
+ * @hide
+ */
+public final class TvInputHardwareInfo implements Parcelable {
+ static final String TAG = "TvInputHardwareInfo";
+
+ // Match hardware/libhardware/include/hardware/tv_input.h
+ public static final int TV_INPUT_TYPE_HDMI = 1;
+ public static final int TV_INPUT_TYPE_BUILT_IN_TUNER = 2;
+ public static final int TV_INPUT_TYPE_PASSTHROUGH = 3;
+
+ public static final Parcelable.Creator<TvInputHardwareInfo> CREATOR =
+ new Parcelable.Creator<TvInputHardwareInfo>() {
+ @Override
+ public TvInputHardwareInfo createFromParcel(Parcel source) {
+ try {
+ TvInputHardwareInfo info = new TvInputHardwareInfo();
+ info.readFromParcel(source);
+ return info;
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating TvInputHardwareInfo from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public TvInputHardwareInfo[] newArray(int size) {
+ return new TvInputHardwareInfo[size];
+ }
+ };
+
+ private int mDeviceId;
+ private int mType;
+ // TODO: Add audio port & audio address for audio service.
+ // TODO: Add HDMI handle for HDMI service.
+
+ public TvInputHardwareInfo() { }
+
+ public TvInputHardwareInfo(int deviceId, int type) {
+ mDeviceId = deviceId;
+ mType = type;
+ }
+
+ public int getDeviceId() {
+ return mDeviceId;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ // Parcelable
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mDeviceId);
+ dest.writeInt(mType);
+ }
+
+ public void readFromParcel(Parcel source) {
+ mDeviceId = source.readInt();
+ mType = source.readInt();
+ }
+}
diff --git a/core/java/android/tv/TvStreamConfig.aidl b/core/java/android/tv/TvStreamConfig.aidl
new file mode 100644
index 0000000..4d0add4
--- /dev/null
+++ b/core/java/android/tv/TvStreamConfig.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.tv;
+
+parcelable TvStreamConfig;
\ No newline at end of file
diff --git a/core/java/android/tv/TvStreamConfig.java b/core/java/android/tv/TvStreamConfig.java
new file mode 100644
index 0000000..03e63b1
--- /dev/null
+++ b/core/java/android/tv/TvStreamConfig.java
@@ -0,0 +1,157 @@
+/*
+ * 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.tv;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class TvStreamConfig implements Parcelable {
+ static final String TAG = TvStreamConfig.class.getSimpleName();
+
+ public final static int STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE = 1;
+ public final static int STREAM_TYPE_BUFFER_PRODUCER = 2;
+
+ private int mStreamId;
+ private int mType;
+ // TODO: Revisit if max widht/height really make sense.
+ private int mMaxWidth;
+ private int mMaxHeight;
+ /**
+ * Generations are incremented once framework receives STREAM_CONFIGURATION_CHANGED event from
+ * HAL module. Framework should throw away outdated configurations and get new configurations
+ * via tv_input_device::get_stream_configurations().
+ */
+ private int mGeneration;
+
+ public static final Parcelable.Creator<TvStreamConfig> CREATOR =
+ new Parcelable.Creator<TvStreamConfig>() {
+ @Override
+ public TvStreamConfig createFromParcel(Parcel source) {
+ try {
+ return new Builder().
+ streamId(source.readInt()).
+ type(source.readInt()).
+ maxWidth(source.readInt()).
+ maxHeight(source.readInt()).
+ generation(source.readInt()).build();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating TvStreamConfig from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public TvStreamConfig[] newArray(int size) {
+ return new TvStreamConfig[size];
+ }
+ };
+
+ private TvStreamConfig() {}
+
+ public int getStreamId() {
+ return mStreamId;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public int getMaxWidth() {
+ return mMaxWidth;
+ }
+
+ public int getMaxHeight() {
+ return mMaxHeight;
+ }
+
+ public int getGeneration() {
+ return mGeneration;
+ }
+
+ // Parcelable
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mStreamId);
+ dest.writeInt(mType);
+ dest.writeInt(mMaxWidth);
+ dest.writeInt(mMaxHeight);
+ dest.writeInt(mGeneration);
+ }
+
+ /**
+ * A helper class for creating a TvStreamConfig object.
+ */
+ public static final class Builder {
+ private Integer mStreamId;
+ private Integer mType;
+ private Integer mMaxWidth;
+ private Integer mMaxHeight;
+ private Integer mGeneration;
+
+ public Builder() {
+ }
+
+ public Builder streamId(int streamId) {
+ mStreamId = streamId;
+ return this;
+ }
+
+ public Builder type(int type) {
+ mType = type;
+ return this;
+ }
+
+ public Builder maxWidth(int maxWidth) {
+ mMaxWidth = maxWidth;
+ return this;
+ }
+
+ public Builder maxHeight(int maxHeight) {
+ mMaxHeight = maxHeight;
+ return this;
+ }
+
+ public Builder generation(int generation) {
+ mGeneration = generation;
+ return this;
+ }
+
+ public TvStreamConfig build() {
+ if (mStreamId == null || mType == null || mMaxWidth == null || mMaxHeight == null
+ || mGeneration == null) {
+ throw new UnsupportedOperationException();
+ }
+
+ TvStreamConfig config = new TvStreamConfig();
+ config.mStreamId = mStreamId;
+ config.mType = mType;
+ config.mMaxWidth = mMaxWidth;
+ config.mMaxHeight = mMaxHeight;
+ config.mGeneration = mGeneration;
+ return config;
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index a272296..424d860 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -41,10 +41,6 @@
* An implementation of Canvas on top of OpenGL ES 2.0.
*/
class GLES20Canvas extends HardwareCanvas {
- // Must match modifiers used in the JNI layer
- private static final int MODIFIER_NONE = 0;
- private static final int MODIFIER_SHADER = 2;
-
private final boolean mOpaque;
protected long mRenderer;
@@ -650,13 +646,8 @@
@Override
public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
Paint paint) {
- int modifiers = setupModifiers(paint, MODIFIER_SHADER);
- try {
- nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom,
- startAngle, sweepAngle, useCenter, paint.mNativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom,
+ startAngle, sweepAngle, useCenter, paint.mNativePaint);
}
private static native void nDrawArc(long renderer, float left, float top,
@@ -672,7 +663,6 @@
public void drawPatch(NinePatch patch, Rect dst, Paint paint) {
Bitmap bitmap = patch.getBitmap();
throwIfCannotDraw(bitmap);
- // Shaders are ignored when drawing patches
final long nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, patch.mNativeChunk,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
@@ -682,7 +672,6 @@
public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
Bitmap bitmap = patch.getBitmap();
throwIfCannotDraw(bitmap);
- // Shaders are ignored when drawing patches
final long nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, patch.mNativeChunk,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
@@ -694,14 +683,8 @@
@Override
public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
throwIfCannotDraw(bitmap);
- // Shaders are ignored when drawing bitmaps
- int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
- try {
- final long nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
}
private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
@@ -710,15 +693,9 @@
@Override
public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
throwIfCannotDraw(bitmap);
- // Shaders are ignored when drawing bitmaps
- int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
- try {
- final long nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
- matrix.native_instance, nativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
+ matrix.native_instance, nativePaint);
}
private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
@@ -727,55 +704,43 @@
@Override
public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
throwIfCannotDraw(bitmap);
- // Shaders are ignored when drawing bitmaps
- int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
- try {
- final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+ 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;
- }
-
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
- dst.left, dst.top, dst.right, dst.bottom, nativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+ 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;
}
+
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+ dst.left, dst.top, dst.right, dst.bottom, nativePaint);
}
@Override
public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
throwIfCannotDraw(bitmap);
- // Shaders are ignored when drawing bitmaps
- int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
- try {
- 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;
- }
-
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
- dst.left, dst.top, dst.right, dst.bottom, nativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+ 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;
}
+
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+ dst.left, dst.top, dst.right, dst.bottom, nativePaint);
}
private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
@@ -805,7 +770,6 @@
throw new ArrayIndexOutOfBoundsException();
}
- // Shaders are ignored when drawing bitmaps
final long nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmap(mRenderer, colors, offset, stride, x, y,
width, height, hasAlpha, nativePaint);
@@ -817,7 +781,6 @@
@Override
public void drawBitmap(int[] colors, int offset, int stride, int x, int y,
int width, int height, boolean hasAlpha, Paint paint) {
- // Shaders are ignored when drawing bitmaps
drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, hasAlpha, paint);
}
@@ -840,14 +803,9 @@
checkRange(colors.length, colorOffset, count);
}
- int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
- try {
- final long nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
- verts, vertOffset, colors, colorOffset, nativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+ nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
+ verts, vertOffset, colors, colorOffset, nativePaint);
}
private static native void nDrawBitmapMesh(long renderer, long bitmap, byte[] buffer,
@@ -856,12 +814,7 @@
@Override
public void drawCircle(float cx, float cy, float radius, Paint paint) {
- int modifiers = setupModifiers(paint, MODIFIER_SHADER);
- try {
- nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint);
}
private static native void nDrawCircle(long renderer, float cx, float cy,
@@ -906,12 +859,7 @@
if ((offset | count) < 0 || offset + count > pts.length) {
throw new IllegalArgumentException("The lines array must contain 4 elements per line.");
}
- int modifiers = setupModifiers(paint, MODIFIER_SHADER);
- try {
- nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
}
private static native void nDrawLines(long renderer, float[] points,
@@ -924,12 +872,7 @@
@Override
public void drawOval(RectF oval, Paint paint) {
- int modifiers = setupModifiers(paint, MODIFIER_SHADER);
- try {
- nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint);
}
private static native void nDrawOval(long renderer, float left, float top,
@@ -944,17 +887,12 @@
@Override
public void drawPath(Path path, Paint paint) {
- int modifiers = setupModifiers(paint, MODIFIER_SHADER);
- try {
- if (path.isSimplePath) {
- if (path.rects != null) {
- nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
- }
- } else {
- nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
+ if (path.isSimplePath) {
+ if (path.rects != null) {
+ nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
}
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+ } else {
+ nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
}
}
@@ -962,12 +900,7 @@
private static native void nDrawRects(long renderer, long region, long paint);
void drawRects(float[] rects, int count, Paint paint) {
- int modifiers = setupModifiers(paint, MODIFIER_SHADER);
- try {
- nDrawRects(mRenderer, rects, count, paint.mNativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawRects(mRenderer, rects, count, paint.mNativePaint);
}
private static native void nDrawRects(long renderer, float[] rects, int count, long paint);
@@ -1029,12 +962,7 @@
public void drawPoints(float[] pts, int offset, int count, Paint paint) {
if (count < 2) return;
- int modifiers = setupModifiers(paint, MODIFIER_SHADER);
- try {
- nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
}
private static native void nDrawPoints(long renderer, float[] points,
@@ -1047,12 +975,7 @@
throw new IndexOutOfBoundsException();
}
- int modifiers = setupModifiers(paint);
- try {
- nDrawPosText(mRenderer, text, index, count, pos, paint.mNativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawPosText(mRenderer, text, index, count, pos, paint.mNativePaint);
}
private static native void nDrawPosText(long renderer, char[] text, int index, int count,
@@ -1065,12 +988,7 @@
throw new ArrayIndexOutOfBoundsException();
}
- int modifiers = setupModifiers(paint);
- try {
- nDrawPosText(mRenderer, text, 0, text.length(), pos, paint.mNativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawPosText(mRenderer, text, 0, text.length(), pos, paint.mNativePaint);
}
private static native void nDrawPosText(long renderer, String text, int start, int end,
@@ -1079,12 +997,7 @@
@Override
public void drawRect(float left, float top, float right, float bottom, Paint paint) {
if (left == right || top == bottom) return;
- int modifiers = setupModifiers(paint, MODIFIER_SHADER);
- try {
- nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
}
private static native void nDrawRect(long renderer, float left, float top,
@@ -1108,12 +1021,7 @@
@Override
public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
Paint paint) {
- int modifiers = setupModifiers(paint, MODIFIER_SHADER);
- try {
- nDrawRoundRect(mRenderer, left, top, right, bottom, rx, ry, paint.mNativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawRoundRect(mRenderer, left, top, right, bottom, rx, ry, paint.mNativePaint);
}
private static native void nDrawRoundRect(long renderer, float left, float top,
@@ -1125,13 +1033,8 @@
throw new IndexOutOfBoundsException();
}
- int modifiers = setupModifiers(paint);
- try {
- nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint,
- paint.mNativeTypeface);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawText(mRenderer, text, index, count, x, y,
+ paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
}
private static native void nDrawText(long renderer, char[] text, int index, int count,
@@ -1139,24 +1042,18 @@
@Override
public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
- int modifiers = setupModifiers(paint);
- try {
- if (text instanceof String || text instanceof SpannedString ||
- text instanceof SpannableString) {
- nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
- paint.mNativePaint, paint.mNativeTypeface);
- } else if (text instanceof GraphicsOperations) {
- ((GraphicsOperations) text).drawText(this, start, end, x, y,
- paint);
- } else {
- char[] buf = TemporaryBuffer.obtain(end - start);
- TextUtils.getChars(text, start, end, buf, 0);
- nDrawText(mRenderer, buf, 0, end - start, x, y,
- paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
- TemporaryBuffer.recycle(buf);
- }
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+ if (text instanceof String || text instanceof SpannedString ||
+ text instanceof SpannableString) {
+ nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
+ paint.mNativePaint, paint.mNativeTypeface);
+ } else if (text instanceof GraphicsOperations) {
+ ((GraphicsOperations) text).drawText(this, start, end, x, y, paint);
+ } else {
+ char[] buf = TemporaryBuffer.obtain(end - start);
+ TextUtils.getChars(text, start, end, buf, 0);
+ nDrawText(mRenderer, buf, 0, end - start, x, y,
+ paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
+ TemporaryBuffer.recycle(buf);
}
}
@@ -1166,13 +1063,8 @@
throw new IndexOutOfBoundsException();
}
- int modifiers = setupModifiers(paint);
- try {
- nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint,
- paint.mNativeTypeface);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawText(mRenderer, text, start, end, x, y,
+ paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
}
private static native void nDrawText(long renderer, String text, int start, int end,
@@ -1180,13 +1072,8 @@
@Override
public void drawText(String text, float x, float y, Paint paint) {
- int modifiers = setupModifiers(paint);
- try {
- nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags,
- paint.mNativePaint, paint.mNativeTypeface);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawText(mRenderer, text, 0, text.length(), x, y,
+ paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
}
@Override
@@ -1196,13 +1083,8 @@
throw new ArrayIndexOutOfBoundsException();
}
- int modifiers = setupModifiers(paint);
- try {
- nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset,
- paint.mBidiFlags, paint.mNativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset,
+ paint.mBidiFlags, paint.mNativePaint);
}
private static native void nDrawTextOnPath(long renderer, char[] text, int index, int count,
@@ -1212,13 +1094,8 @@
public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
if (text.length() == 0) return;
- int modifiers = setupModifiers(paint);
- try {
- nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset,
- paint.mBidiFlags, paint.mNativePaint);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset,
+ paint.mBidiFlags, paint.mNativePaint);
}
private static native void nDrawTextOnPath(long renderer, String text, int start, int end,
@@ -1234,13 +1111,8 @@
throw new IllegalArgumentException("Unknown direction: " + dir);
}
- int modifiers = setupModifiers(paint);
- try {
- nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
- paint.mNativePaint, paint.mNativeTypeface);
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
- }
+ nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
+ paint.mNativePaint, paint.mNativeTypeface);
}
private static native void nDrawTextRun(long renderer, char[] text, int index, int count,
@@ -1253,27 +1125,22 @@
throw new IndexOutOfBoundsException();
}
- int modifiers = setupModifiers(paint);
- try {
- int flags = dir == 0 ? 0 : 1;
- if (text instanceof String || text instanceof SpannedString ||
- text instanceof SpannableString) {
- nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
- contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
- } else if (text instanceof GraphicsOperations) {
- ((GraphicsOperations) text).drawTextRun(this, start, end,
- contextStart, contextEnd, x, y, flags, paint);
- } else {
- int contextLen = contextEnd - contextStart;
- int len = end - start;
- char[] buf = TemporaryBuffer.obtain(contextLen);
- TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
- nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
- x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
- TemporaryBuffer.recycle(buf);
- }
- } finally {
- if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+ int flags = dir == 0 ? 0 : 1;
+ if (text instanceof String || text instanceof SpannedString ||
+ text instanceof SpannableString) {
+ nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
+ contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
+ } else if (text instanceof GraphicsOperations) {
+ ((GraphicsOperations) text).drawTextRun(this, start, end,
+ contextStart, contextEnd, x, y, flags, paint);
+ } else {
+ int contextLen = contextEnd - contextStart;
+ int len = end - start;
+ char[] buf = TemporaryBuffer.obtain(contextLen);
+ TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
+ nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
+ x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
+ TemporaryBuffer.recycle(buf);
}
}
@@ -1286,40 +1153,4 @@
int indexOffset, int indexCount, Paint paint) {
// TODO: Implement
}
-
- private int setupModifiers(Bitmap b, Paint paint) {
- if (b.getConfig() != Bitmap.Config.ALPHA_8) {
- return MODIFIER_NONE;
- } else {
- return setupModifiers(paint);
- }
- }
-
- private int setupModifiers(Paint paint) {
- int modifiers = MODIFIER_NONE;
-
- final Shader shader = paint.getShader();
- if (shader != null) {
- nSetupShader(mRenderer, shader.native_shader);
- modifiers |= MODIFIER_SHADER;
- }
-
- return modifiers;
- }
-
- private int setupModifiers(Paint paint, int flags) {
- int modifiers = MODIFIER_NONE;
-
- final Shader shader = paint.getShader();
- if (shader != null && (flags & MODIFIER_SHADER) != 0) {
- nSetupShader(mRenderer, shader.native_shader);
- modifiers |= MODIFIER_SHADER;
- }
-
- return modifiers;
- }
-
- private static native void nSetupShader(long renderer, long shader);
-
- private static native void nResetModifiers(long renderer, int modifiers);
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index a902ce7..3c4d83f 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -579,6 +579,12 @@
abstract void fence();
/**
+ * Called by {@link ViewRootImpl} when a new performTraverals is scheduled.
+ */
+ public void notifyFramePending() {
+ }
+
+ /**
* Describes a series of frames that should be drawn on screen as a graph.
* Each frame is composed of 1 or more elements.
*/
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 704d516..8417887 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -291,6 +291,11 @@
}
@Override
+ public void notifyFramePending() {
+ nNotifyFramePending(mNativeProxy);
+ }
+
+ @Override
protected void finalize() throws Throwable {
try {
nDeleteProxy(mNativeProxy);
@@ -364,4 +369,5 @@
private static native void nDestroyLayer(long nativeProxy, long layer);
private static native void nFence(long nativeProxy);
+ private static native void nNotifyFramePending(long nativeProxy);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0f21c1d..6396781 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9236,6 +9236,30 @@
}
/**
+ * Request unbuffered dispatch of the given stream of MotionEvents to this View.
+ *
+ * Until this View receives a corresponding {@link MotionEvent#ACTION_UP}, ask that the input
+ * system not batch {@link MotionEvent}s but instead deliver them as soon as they're
+ * available. This method should only be called for touch events.
+ *
+ * <p class="note">This api is not intended for most applications. Buffered dispatch
+ * provides many of benefits, and just requesting unbuffered dispatch on most MotionEvent
+ * streams will not improve your input latency. Side effects include: increased latency,
+ * jittery scrolls and inability to take advantage of system resampling. Talk to your input
+ * professional to see if {@link #requestUnbufferedDispatch(MotionEvent)} is right for
+ * you.</p>
+ */
+ public final void requestUnbufferedDispatch(MotionEvent event) {
+ final int action = event.getAction();
+ if (mAttachInfo == null
+ || action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_MOVE
+ || !event.isTouchEvent()) {
+ return;
+ }
+ mAttachInfo.mUnbufferedDispatchRequested = true;
+ }
+
+ /**
* Set flags controlling behavior of this view.
*
* @param flags Constant indicating the value which should be set
@@ -19760,6 +19784,12 @@
boolean mInTouchMode;
/**
+ * Indicates whether the view has requested unbuffered input dispatching for the current
+ * event stream.
+ */
+ boolean mUnbufferedDispatchRequested;
+
+ /**
* Indicates that ViewAncestor should trigger a global layout change
* the next time it performs a traversal
*/
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 799a406..c3bf2953 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -230,6 +230,7 @@
QueuedInputEvent mPendingInputEventTail;
int mPendingInputEventCount;
boolean mProcessInputEventsScheduled;
+ boolean mUnbufferedInputDispatch;
String mPendingInputEventQueueLengthCounterName = "pq";
InputStage mFirstInputStage;
@@ -999,13 +1000,27 @@
}
}
+ /**
+ * Notifies the HardwareRenderer that a new frame will be coming soon.
+ * Currently only {@link ThreadedRenderer} cares about this, and uses
+ * this knowledge to adjust the scheduling of off-thread animations
+ */
+ void notifyRendererOfFramePending() {
+ if (mAttachInfo.mHardwareRenderer != null) {
+ mAttachInfo.mHardwareRenderer.notifyFramePending();
+ }
+ }
+
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
- scheduleConsumeBatchedInput();
+ if (!mUnbufferedInputDispatch) {
+ scheduleConsumeBatchedInput();
+ }
+ notifyRendererOfFramePending();
}
}
@@ -2604,7 +2619,7 @@
}
final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
- final Rect bounds = mView.mAttachInfo.mTmpInvalRect;
+ final Rect bounds = mAttachInfo.mTmpInvalRect;
if (provider == null) {
host.getBoundsOnScreen(bounds);
} else if (mAccessibilityFocusedVirtualView != null) {
@@ -3886,6 +3901,18 @@
}
}
+ @Override
+ protected void onDeliverToNext(QueuedInputEvent q) {
+ if (mUnbufferedInputDispatch
+ && q.mEvent instanceof MotionEvent
+ && ((MotionEvent)q.mEvent).isTouchEvent()
+ && isTerminalInputEvent(q.mEvent)) {
+ mUnbufferedInputDispatch = false;
+ scheduleConsumeBatchedInput();
+ }
+ super.onDeliverToNext(q);
+ }
+
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
@@ -3998,10 +4025,15 @@
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
- if (mView.dispatchPointerEvent(event)) {
- return FINISH_HANDLED;
+ mAttachInfo.mUnbufferedDispatchRequested = false;
+ boolean handled = mView.dispatchPointerEvent(event);
+ if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
+ mUnbufferedInputDispatch = true;
+ if (mConsumeBatchedInputScheduled) {
+ scheduleConsumeBatchedInputImmediately();
+ }
}
- return FORWARD;
+ return handled ? FINISH_HANDLED : FORWARD;
}
private int processTrackballEvent(QueuedInputEvent q) {
@@ -5266,6 +5298,8 @@
writer.print(" mRemoved="); writer.println(mRemoved);
writer.print(innerPrefix); writer.print("mConsumeBatchedInputScheduled=");
writer.println(mConsumeBatchedInputScheduled);
+ writer.print(innerPrefix); writer.print("mConsumeBatchedInputImmediatelyScheduled=");
+ writer.println(mConsumeBatchedInputImmediatelyScheduled);
writer.print(innerPrefix); writer.print("mPendingInputEventCount=");
writer.println(mPendingInputEventCount);
writer.print(innerPrefix); writer.print("mProcessInputEventsScheduled=");
@@ -5676,6 +5710,7 @@
private void finishInputEvent(QueuedInputEvent q) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
+
if (q.mReceiver != null) {
boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
q.mReceiver.finishInputEvent(q.mEvent, handled);
@@ -5715,15 +5750,25 @@
}
}
+ void scheduleConsumeBatchedInputImmediately() {
+ if (!mConsumeBatchedInputImmediatelyScheduled) {
+ unscheduleConsumeBatchedInput();
+ mConsumeBatchedInputImmediatelyScheduled = true;
+ mHandler.post(mConsumeBatchedInputImmediatelyRunnable);
+ }
+ }
+
void doConsumeBatchedInput(long frameTimeNanos) {
if (mConsumeBatchedInputScheduled) {
mConsumeBatchedInputScheduled = false;
if (mInputEventReceiver != null) {
- if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)) {
+ if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)
+ && frameTimeNanos != -1) {
// If we consumed a batch here, we want to go ahead and schedule the
// consumption of batched input events on the next frame. Otherwise, we would
// wait until we have more input events pending and might get starved by other
- // things occurring in the process.
+ // things occurring in the process. If the frame time is -1, however, then
+ // we're in a non-batching mode, so there's no need to schedule this.
scheduleConsumeBatchedInput();
}
}
@@ -5751,7 +5796,11 @@
@Override
public void onBatchedInputEventPending() {
- scheduleConsumeBatchedInput();
+ if (mUnbufferedInputDispatch) {
+ super.onBatchedInputEventPending();
+ } else {
+ scheduleConsumeBatchedInput();
+ }
}
@Override
@@ -5772,6 +5821,16 @@
new ConsumeBatchedInputRunnable();
boolean mConsumeBatchedInputScheduled;
+ final class ConsumeBatchedInputImmediatelyRunnable implements Runnable {
+ @Override
+ public void run() {
+ doConsumeBatchedInput(-1);
+ }
+ }
+ final ConsumeBatchedInputImmediatelyRunnable mConsumeBatchedInputImmediatelyRunnable =
+ new ConsumeBatchedInputImmediatelyRunnable();
+ boolean mConsumeBatchedInputImmediatelyScheduled;
+
final class InvalidateOnAnimationRunnable implements Runnable {
private boolean mPosted;
private final ArrayList<View> mViews = new ArrayList<View>();
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 4fde1e4..1bb20c9 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1144,6 +1144,12 @@
public void setLastInputMethodWindowLw(WindowState ime, WindowState target);
/**
+ * Show the recents task list app.
+ * @hide
+ */
+ public void showRecentApps();
+
+ /**
* @return The current height of the input method window.
*/
public int getInputMethodWindowVisibleHeightLw();
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 1152e17..43f623b 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -256,6 +256,16 @@
}
@Override
+ public void invalidateDrawable(Drawable dr) {
+ super.invalidateDrawable(dr);
+
+ if (dr == mThumb) {
+ // Handle changes to thumb width and height.
+ requestLayout();
+ }
+ }
+
+ @Override
void onProgressRefresh(float scale, boolean fromUser) {
super.onProgressRefresh(scale, fromUser);
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 1533510..3ae9508 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -24,6 +24,7 @@
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
+import android.view.RemotableViewMethod;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -156,10 +157,36 @@
mCheckMarkWidth = 0;
}
mCheckMarkDrawable = d;
- // Do padding resolution. This will call internalSetPadding() and do a requestLayout() if needed.
+
+ // Do padding resolution. This will call internalSetPadding() and do a
+ // requestLayout() if needed.
resolvePadding();
}
+ @RemotableViewMethod
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+
+ if (mCheckMarkDrawable != null) {
+ mCheckMarkDrawable.setVisible(visibility == VISIBLE, false);
+ }
+ }
+
+ @Override
+ public void jumpDrawablesToCurrentState() {
+ super.jumpDrawablesToCurrentState();
+
+ if (mCheckMarkDrawable != null) {
+ mCheckMarkDrawable.jumpToCurrentState();
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return who == mCheckMarkDrawable || super.verifyDrawable(who);
+ }
+
/**
* Gets the checkmark drawable
*
@@ -249,6 +276,11 @@
}
checkMarkDrawable.setBounds(mScrollX + left, top, mScrollX + right, bottom);
checkMarkDrawable.draw(canvas);
+
+ final Drawable background = getBackground();
+ if (background != null) {
+ background.setHotspotBounds(mScrollX + left, top, mScrollX + right, bottom);
+ }
}
}
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 8511601..defc26c 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -104,14 +104,16 @@
*
* <h4>Excess Space Distribution</h4>
*
- * GridLayout's distribution of excess space is based on <em>priority</em>
- * rather than <em>weight</em>.
+ * As of API 21, GridLayout's distribution of excess space accomodates the principle of weight.
+ * In the event that no weights are specified, the previous conventions are respected and
+ * columns and rows are taken as flexible if their views specify some form of alignment
+ * within their groups.
* <p>
- * A child's ability to stretch is inferred from the alignment properties of
- * its row and column groups (which are typically set by setting the
- * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters).
- * If alignment was defined along a given axis then the component
- * is taken as <em>flexible</em> in that direction. If no alignment was set,
+ * The flexibility of a view is therefore influenced by its alignment which is,
+ * in turn, typically defined by setting the
+ * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters.
+ * If either a weight or alignment were defined along a given axis then the component
+ * is taken as <em>flexible</em> in that direction. If no weight or alignment was set,
* the component is instead assumed to be <em>inflexible</em>.
* <p>
* Multiple components in the same row or column group are
@@ -122,12 +124,16 @@
* elements is flexible if <em>one</em> of its elements is flexible.
* <p>
* To make a column stretch, make sure all of the components inside it define a
- * gravity. To prevent a column from stretching, ensure that one of the components
- * in the column does not define a gravity.
+ * weight or a gravity. To prevent a column from stretching, ensure that one of the components
+ * in the column does not define a weight or a gravity.
* <p>
* When the principle of flexibility does not provide complete disambiguation,
* GridLayout's algorithms favour rows and columns that are closer to its <em>right</em>
- * and <em>bottom</em> edges.
+ * and <em>bottom</em> edges. To be more precise, GridLayout treats each of its layout
+ * parameters as a constraint in the a set of variables that define the grid-lines along a
+ * given axis. During layout, GridLayout solves the constraints so as to return the unique
+ * solution to those constraints for which all variables are less-than-or-equal-to
+ * the corresponding value in any other valid solution.
*
* <h4>Interpretation of GONE</h4>
*
@@ -140,18 +146,6 @@
* had never been added to it.
* These statements apply equally to rows as well as columns, and to groups of rows or columns.
*
- * <h5>Limitations</h5>
- *
- * GridLayout does not provide support for the principle of <em>weight</em>, as defined in
- * {@link LinearLayout.LayoutParams#weight}. In general, it is not therefore possible
- * to configure a GridLayout to distribute excess space between multiple components.
- * <p>
- * Some common use-cases may nevertheless be accommodated as follows.
- * To place equal amounts of space around a component in a cell group;
- * use {@link #CENTER} alignment (or {@link LayoutParams#setGravity(int) gravity}).
- * For complete control over excess space distribution in a row or column;
- * use a {@link LinearLayout} subview to hold the components in the associated cell group.
- * When using either of these techniques, bear in mind that cell groups may be defined to overlap.
* <p>
* See {@link GridLayout.LayoutParams} for a full description of the
* layout parameters used by GridLayout.
@@ -1018,6 +1012,8 @@
LayoutParams lp = getLayoutParams(c);
if (firstPass) {
measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
+ mHorizontalAxis.recordOriginalMeasurement(i);
+ mVerticalAxis.recordOriginalMeasurement(i);
} else {
boolean horizontal = (mOrientation == HORIZONTAL);
Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
@@ -1245,6 +1241,11 @@
public int[] locations;
public boolean locationsValid = false;
+ public boolean hasWeights;
+ public boolean hasWeightsValid = false;
+ public int[] originalMeasurements;
+ public int[] deltas;
+
boolean orderPreserved = DEFAULT_ORDER_PRESERVED;
private MutableInt parentMin = new MutableInt(0);
@@ -1321,7 +1322,10 @@
// we must include views that are GONE here, see introductory javadoc
LayoutParams lp = getLayoutParams(c);
Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
- groupBounds.getValue(i).include(GridLayout.this, c, spec, this);
+ int size = (spec.weight == 0) ?
+ getMeasurementIncludingMargin(c, horizontal) :
+ getOriginalMeasurements()[i] + getDeltas()[i];
+ groupBounds.getValue(i).include(GridLayout.this, c, spec, this, size);
}
}
@@ -1693,8 +1697,94 @@
return trailingMargins;
}
- private void computeLocations(int[] a) {
+ private void solve(int[] a) {
solve(getArcs(), a);
+ }
+
+ private boolean computeHasWeights() {
+ for (int i = 0, N = getChildCount(); i < N; i++) {
+ LayoutParams lp = getLayoutParams(getChildAt(i));
+ Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
+ if (spec.weight != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasWeights() {
+ if (!hasWeightsValid) {
+ hasWeights = computeHasWeights();
+ hasWeightsValid = true;
+ }
+ return hasWeights;
+ }
+
+ public int[] getOriginalMeasurements() {
+ if (originalMeasurements == null) {
+ originalMeasurements = new int[getChildCount()];
+ }
+ return originalMeasurements;
+ }
+
+ private void recordOriginalMeasurement(int i) {
+ if (hasWeights()) {
+ getOriginalMeasurements()[i] = getMeasurementIncludingMargin(getChildAt(i), horizontal);
+ }
+ }
+
+ public int[] getDeltas() {
+ if (deltas == null) {
+ deltas = new int[getChildCount()];
+ }
+ return deltas;
+ }
+
+ private void shareOutDelta() {
+ int totalDelta = 0;
+ float totalWeight = 0;
+ for (int i = 0, N = getChildCount(); i < N; i++) {
+ View c = getChildAt(i);
+ LayoutParams lp = getLayoutParams(c);
+ Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
+ float weight = spec.weight;
+ if (weight != 0) {
+ int delta = getMeasurement(c, horizontal) - getOriginalMeasurements()[i];
+ totalDelta += delta;
+ totalWeight += weight;
+ }
+ }
+ for (int i = 0, N = getChildCount(); i < N; i++) {
+ LayoutParams lp = getLayoutParams(getChildAt(i));
+ Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
+ float weight = spec.weight;
+ if (weight != 0) {
+ int delta = Math.round((weight * totalDelta / totalWeight));
+ deltas[i] = delta;
+ // the two adjustments below are to counter the above rounding and avoid off-by-ones at the end
+ totalDelta -= delta;
+ totalWeight -= weight;
+ }
+ }
+ }
+
+ private void solveAndDistributeSpace(int[] a) {
+ Arrays.fill(getDeltas(), 0);
+ solve(a);
+ shareOutDelta();
+ arcsValid = false;
+ forwardLinksValid = false;
+ backwardLinksValid = false;
+ groupBoundsValid = false;
+ solve(a);
+ }
+
+ private void computeLocations(int[] a) {
+ if (!hasWeights()) {
+ solve(a);
+ } else {
+ solveAndDistributeSpace(a);
+ }
if (!orderPreserved) {
// Solve returns the smallest solution to the constraint system for which all
// values are positive. One value is therefore zero - though if the row/col
@@ -1777,6 +1867,10 @@
locations = null;
+ originalMeasurements = null;
+ deltas = null;
+ hasWeightsValid = false;
+
invalidateValues();
}
@@ -1810,6 +1904,9 @@
* both aspects of alignment within the cell group. It is also possible to specify a child's
* alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
* method.
+ * <p>
+ * The weight property is also included in Spec and specifies the proportion of any
+ * excess space that is due to the associated view.
*
* <h4>WRAP_CONTENT and MATCH_PARENT</h4>
*
@@ -1851,9 +1948,11 @@
* <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
* <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
* <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
+ * <li>{@link #rowSpec}<code>.weight</code> = 0 </li>
* <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
* <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
* <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li>
+ * <li>{@link #columnSpec}<code>.weight</code> = 0 </li>
* </ul>
*
* See {@link GridLayout} for a more complete description of the conventions
@@ -1861,8 +1960,10 @@
*
* @attr ref android.R.styleable#GridLayout_Layout_layout_row
* @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
+ * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight
* @attr ref android.R.styleable#GridLayout_Layout_layout_column
* @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
+ * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight
* @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
*/
public static class LayoutParams extends MarginLayoutParams {
@@ -1889,9 +1990,11 @@
private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
+ private static final int COLUMN_WEIGHT = R.styleable.GridLayout_Layout_layout_columnWeight;
private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
+ private static final int ROW_WEIGHT = R.styleable.GridLayout_Layout_layout_rowWeight;
private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
@@ -2034,11 +2137,13 @@
int column = a.getInt(COLUMN, DEFAULT_COLUMN);
int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
- this.columnSpec = spec(column, colSpan, getAlignment(gravity, true));
+ float colWeight = a.getFloat(COLUMN_WEIGHT, Spec.DEFAULT_WEIGHT);
+ this.columnSpec = spec(column, colSpan, getAlignment(gravity, true), colWeight);
int row = a.getInt(ROW, DEFAULT_ROW);
int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
- this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false));
+ float rowWeight = a.getFloat(ROW_WEIGHT, Spec.DEFAULT_WEIGHT);
+ this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false), rowWeight);
} finally {
a.recycle();
}
@@ -2273,10 +2378,9 @@
return before - a.getAlignmentValue(c, size, gl.getLayoutMode());
}
- protected final void include(GridLayout gl, View c, Spec spec, Axis axis) {
+ protected final void include(GridLayout gl, View c, Spec spec, Axis axis, int size) {
this.flexibility &= spec.getFlexibility();
boolean horizontal = axis.horizontal;
- int size = gl.getMeasurementIncludingMargin(c, horizontal);
Alignment alignment = gl.getAlignment(spec.alignment, horizontal);
// todo test this works correctly when the returned value is UNDEFINED
int before = alignment.getAlignmentValue(c, size, gl.getLayoutMode());
@@ -2401,36 +2505,43 @@
* <li>{@link #spec(int, int)}</li>
* <li>{@link #spec(int, Alignment)}</li>
* <li>{@link #spec(int, int, Alignment)}</li>
+ * <li>{@link #spec(int, float)}</li>
+ * <li>{@link #spec(int, int, float)}</li>
+ * <li>{@link #spec(int, Alignment, float)}</li>
+ * <li>{@link #spec(int, int, Alignment, float)}</li>
* </ul>
*
*/
public static class Spec {
static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
+ static final float DEFAULT_WEIGHT = 0;
final boolean startDefined;
final Interval span;
final Alignment alignment;
+ final float weight;
- private Spec(boolean startDefined, Interval span, Alignment alignment) {
+ private Spec(boolean startDefined, Interval span, Alignment alignment, float weight) {
this.startDefined = startDefined;
this.span = span;
this.alignment = alignment;
+ this.weight = weight;
}
- private Spec(boolean startDefined, int start, int size, Alignment alignment) {
- this(startDefined, new Interval(start, start + size), alignment);
+ private Spec(boolean startDefined, int start, int size, Alignment alignment, float weight) {
+ this(startDefined, new Interval(start, start + size), alignment, weight);
}
final Spec copyWriteSpan(Interval span) {
- return new Spec(startDefined, span, alignment);
+ return new Spec(startDefined, span, alignment, weight);
}
final Spec copyWriteAlignment(Alignment alignment) {
- return new Spec(startDefined, span, alignment);
+ return new Spec(startDefined, span, alignment, weight);
}
final int getFlexibility() {
- return (alignment == UNDEFINED_ALIGNMENT) ? INFLEXIBLE : CAN_STRETCH;
+ return (alignment == UNDEFINED_ALIGNMENT && weight == 0) ? INFLEXIBLE : CAN_STRETCH;
}
/**
@@ -2478,6 +2589,7 @@
* <ul>
* <li> {@code spec.span = [start, start + size]} </li>
* <li> {@code spec.alignment = alignment} </li>
+ * <li> {@code spec.weight = weight} </li>
* </ul>
* <p>
* To leave the start index undefined, use the value {@link #UNDEFINED}.
@@ -2485,9 +2597,55 @@
* @param start the start
* @param size the size
* @param alignment the alignment
+ * @param weight the weight
+ */
+ public static Spec spec(int start, int size, Alignment alignment, float weight) {
+ return new Spec(start != UNDEFINED, start, size, alignment, weight);
+ }
+
+ /**
+ * Equivalent to: {@code spec(start, 1, alignment, weight)}.
+ *
+ * @param start the start
+ * @param alignment the alignment
+ * @param weight the weight
+ */
+ public static Spec spec(int start, Alignment alignment, float weight) {
+ return spec(start, 1, alignment, weight);
+ }
+
+ /**
+ * Equivalent to: {@code spec(start, 1, default_alignment, weight)} -
+ * where {@code default_alignment} is specified in
+ * {@link android.widget.GridLayout.LayoutParams}.
+ *
+ * @param start the start
+ * @param size the size
+ * @param weight the weight
+ */
+ public static Spec spec(int start, int size, float weight) {
+ return spec(start, size, UNDEFINED_ALIGNMENT, weight);
+ }
+
+ /**
+ * Equivalent to: {@code spec(start, 1, weight)}.
+ *
+ * @param start the start
+ * @param weight the weight
+ */
+ public static Spec spec(int start, float weight) {
+ return spec(start, 1, weight);
+ }
+
+ /**
+ * Equivalent to: {@code spec(start, size, alignment, 0f)}.
+ *
+ * @param start the start
+ * @param size the size
+ * @param alignment the alignment
*/
public static Spec spec(int start, int size, Alignment alignment) {
- return new Spec(start != UNDEFINED, start, size, alignment);
+ return spec(start, size, alignment, Spec.DEFAULT_WEIGHT);
}
/**
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index ad1a023..c5c6e64 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -951,9 +951,8 @@
final int[] myDrawableState = getDrawableState();
- if (mThumbDrawable != null && mThumbDrawable.setState(myDrawableState)) {
- // Handle changes to thumb width and height.
- requestLayout();
+ if (mThumbDrawable != null) {
+ mThumbDrawable.setState(myDrawableState);
}
if (mTrackDrawable != null) {
@@ -964,6 +963,16 @@
}
@Override
+ public void invalidateDrawable(Drawable drawable) {
+ super.invalidateDrawable(drawable);
+
+ if (drawable == mThumbDrawable) {
+ // Handle changes to thumb width and height.
+ requestLayout();
+ }
+ }
+
+ @Override
protected boolean verifyDrawable(Drawable who) {
return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
}
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 664f9db..5547a10 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -1026,7 +1026,7 @@
? dialog.mSingleChoiceItemLayout : dialog.mListItemLayout;
if (mCursor == null) {
adapter = (mAdapter != null) ? mAdapter
- : new ArrayAdapter<CharSequence>(mContext, layout, R.id.text1, mItems);
+ : new CheckedItemAdapter(mContext, layout, R.id.text1, mItems);
} else {
adapter = new SimpleCursorAdapter(mContext, layout,
mCursor, new String[]{mLabelColumn}, new int[]{R.id.text1});
@@ -1081,4 +1081,20 @@
}
}
+ private static class CheckedItemAdapter extends ArrayAdapter<CharSequence> {
+ public CheckedItemAdapter(Context context, int resource, int textViewResourceId,
+ CharSequence[] objects) {
+ super(context, resource, textViewResourceId, objects);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 47ef65a..01e5d40 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -35,8 +35,8 @@
/*
- * This is used in conjunction with DevicePolicyManager.setForwardingIntents to enable intents to be
- * passed in and out of a managed profile.
+ * This is used in conjunction with the {@link setCrossProfileIntentFilter} method of
+ * {@link DevicePolicyManager} to enable intents to be passed in and out of a managed profile.
*/
public class IntentForwarderActivity extends Activity {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 591267e..183dd05 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -484,8 +484,7 @@
mList.clear();
if (mBaseResolveList != null) {
- currentResolveList = mBaseResolveList;
- mOrigResolveList = null;
+ currentResolveList = mOrigResolveList = mBaseResolveList;
} else {
currentResolveList = mOrigResolveList = mPm.queryIntentActivities(
mIntent, PackageManager.MATCH_DEFAULT_ONLY
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index 495d5c688..df96488 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Slog;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -211,54 +212,196 @@
}
}
- private final InputMethodSettings mSettings;
- private InputMethodAndSubtypeList mSubtypeList;
+ private static int calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype) {
+ return subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
+ subtype.hashCode()) : NOT_A_SUBTYPE_ID;
+ }
- @VisibleForTesting
- public static ImeSubtypeListItem getNextInputMethodLockedImpl(List<ImeSubtypeListItem> imList,
- boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
- if (imi == null) {
- return null;
+ private static class StaticRotationList {
+ private final List<ImeSubtypeListItem> mImeSubtypeList;
+ public StaticRotationList(final List<ImeSubtypeListItem> imeSubtypeList) {
+ mImeSubtypeList = imeSubtypeList;
}
- if (imList.size() <= 1) {
- return null;
- }
- // Here we have two rotation groups, depending on the returned boolean value of
- // {@link InputMethodInfo#supportsSwitchingToNextInputMethod()}.
- final boolean expectedValueOfSupportsSwitchingToNextInputMethod =
- imi.supportsSwitchingToNextInputMethod();
- final int N = imList.size();
- final int currentSubtypeId =
- subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
- subtype.hashCode()) : NOT_A_SUBTYPE_ID;
- for (int i = 0; i < N; ++i) {
- final ImeSubtypeListItem isli = imList.get(i);
- // Skip until the current IME/subtype is found.
- if (!isli.mImi.equals(imi) || isli.mSubtypeId != currentSubtypeId) {
- continue;
- }
- // Found the current IME/subtype. Start searching the next IME/subtype from here.
- for (int j = 0; j < N - 1; ++j) {
- final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N);
- // Skip if the candidate doesn't belong to the expected rotation group.
- if (expectedValueOfSupportsSwitchingToNextInputMethod !=
- candidate.mImi.supportsSwitchingToNextInputMethod()) {
- continue;
+
+ /**
+ * Returns the index of the specified input method and subtype in the given list.
+ * @param imi The {@link InputMethodInfo} to be searched.
+ * @param subtype The {@link InputMethodSubtype} to be searched. null if the input method
+ * does not have a subtype.
+ * @return The index in the given list. -1 if not found.
+ */
+ private int getIndex(InputMethodInfo imi, InputMethodSubtype subtype) {
+ final int currentSubtypeId = calculateSubtypeId(imi, subtype);
+ final int N = mImeSubtypeList.size();
+ for (int i = 0; i < N; ++i) {
+ final ImeSubtypeListItem isli = mImeSubtypeList.get(i);
+ // Skip until the current IME/subtype is found.
+ if (imi.equals(isli.mImi) && isli.mSubtypeId == currentSubtypeId) {
+ return i;
}
+ }
+ return -1;
+ }
+
+ public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
+ InputMethodInfo imi, InputMethodSubtype subtype) {
+ if (imi == null) {
+ return null;
+ }
+ if (mImeSubtypeList.size() <= 1) {
+ return null;
+ }
+ final int currentIndex = getIndex(imi, subtype);
+ if (currentIndex < 0) {
+ return null;
+ }
+ final int N = mImeSubtypeList.size();
+ for (int offset = 1; offset < N; ++offset) {
+ // Start searching the next IME/subtype from the next of the current index.
+ final int candidateIndex = (currentIndex + offset) % N;
+ final ImeSubtypeListItem candidate = mImeSubtypeList.get(candidateIndex);
// Skip if searching inside the current IME only, but the candidate is not
// the current IME.
- if (onlyCurrentIme && !candidate.mImi.equals(imi)) {
+ if (onlyCurrentIme && !imi.equals(candidate.mImi)) {
continue;
}
return candidate;
}
- // No appropriate IME/subtype is found in the list. Give up.
return null;
}
- // The current IME/subtype is not found in the list. Give up.
- return null;
}
+ private static class DynamicRotationList {
+ private static final String TAG = DynamicRotationList.class.getSimpleName();
+ private final List<ImeSubtypeListItem> mImeSubtypeList;
+ private final int[] mUsageHistoryOfSubtypeListItemIndex;
+
+ public DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) {
+ mImeSubtypeList = imeSubtypeListItems;
+ mUsageHistoryOfSubtypeListItemIndex = new int[mImeSubtypeList.size()];
+ final int N = mImeSubtypeList.size();
+ for (int i = 0; i < N; i++) {
+ mUsageHistoryOfSubtypeListItemIndex[i] = i;
+ }
+ }
+
+ /**
+ * Returns the index of the specified object in
+ * {@link #mUsageHistoryOfSubtypeListItemIndex}.
+ * <p>We call the index of {@link #mUsageHistoryOfSubtypeListItemIndex} as "Usage Rank"
+ * so as not to be confused with the index in {@link #mImeSubtypeList}.
+ * @return -1 when the specified item doesn't belong to {@link #mImeSubtypeList} actually.
+ */
+ private int getUsageRank(final InputMethodInfo imi, InputMethodSubtype subtype) {
+ final int currentSubtypeId = calculateSubtypeId(imi, subtype);
+ final int N = mUsageHistoryOfSubtypeListItemIndex.length;
+ for (int usageRank = 0; usageRank < N; usageRank++) {
+ final int subtypeListItemIndex = mUsageHistoryOfSubtypeListItemIndex[usageRank];
+ final ImeSubtypeListItem subtypeListItem =
+ mImeSubtypeList.get(subtypeListItemIndex);
+ if (subtypeListItem.mImi.equals(imi) &&
+ subtypeListItem.mSubtypeId == currentSubtypeId) {
+ return usageRank;
+ }
+ }
+ // Not found in the known IME/Subtype list.
+ return -1;
+ }
+
+ public void onUserAction(InputMethodInfo imi, InputMethodSubtype subtype) {
+ final int currentUsageRank = getUsageRank(imi, subtype);
+ // Do nothing if currentUsageRank == -1 (not found), or currentUsageRank == 0
+ if (currentUsageRank <= 0) {
+ return;
+ }
+ final int currentItemIndex = mUsageHistoryOfSubtypeListItemIndex[currentUsageRank];
+ System.arraycopy(mUsageHistoryOfSubtypeListItemIndex, 0,
+ mUsageHistoryOfSubtypeListItemIndex, 1, currentUsageRank);
+ mUsageHistoryOfSubtypeListItemIndex[0] = currentItemIndex;
+ }
+
+ public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
+ InputMethodInfo imi, InputMethodSubtype subtype) {
+ int currentUsageRank = getUsageRank(imi, subtype);
+ if (currentUsageRank < 0) {
+ if (DEBUG) {
+ Slog.d(TAG, "IME/subtype is not found: " + imi.getId() + ", " + subtype);
+ }
+ return null;
+ }
+ final int N = mUsageHistoryOfSubtypeListItemIndex.length;
+ for (int i = 1; i < N; i++) {
+ final int subtypeListItemRank = (currentUsageRank + i) % N;
+ final int subtypeListItemIndex =
+ mUsageHistoryOfSubtypeListItemIndex[subtypeListItemRank];
+ final ImeSubtypeListItem subtypeListItem =
+ mImeSubtypeList.get(subtypeListItemIndex);
+ if (onlyCurrentIme && !imi.equals(subtypeListItem.mImi)) {
+ continue;
+ }
+ return subtypeListItem;
+ }
+ return null;
+ }
+ }
+
+ @VisibleForTesting
+ public static class ControllerImpl {
+ // TODO: Switch to DynamicRotationList for smarter rotation.
+ private final StaticRotationList mSwitchingAwareSubtypeList;
+ private final StaticRotationList mSwitchingUnawareSubtypeList;
+
+ public ControllerImpl(final List<ImeSubtypeListItem> sortedItems) {
+ mSwitchingAwareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems,
+ true /* supportsSwitchingToNextInputMethod */));
+ mSwitchingUnawareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems,
+ false /* supportsSwitchingToNextInputMethod */));
+ }
+
+ public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi,
+ InputMethodSubtype subtype) {
+ if (imi == null) {
+ return null;
+ }
+ if (imi.supportsSwitchingToNextInputMethod()) {
+ return mSwitchingAwareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi,
+ subtype);
+ } else {
+ return mSwitchingUnawareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi,
+ subtype);
+ }
+ }
+
+ public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
+ if (imi == null) {
+ return;
+ }
+ // TODO: Enable the following code when DynamicRotationList is enabled.
+ // if (imi.supportsSwitchingToNextInputMethod()) {
+ // mSwitchingAwareSubtypeList.onUserAction(imi, subtype);
+ // }
+ }
+
+ private static List<ImeSubtypeListItem> filterImeSubtypeList(
+ final List<ImeSubtypeListItem> items,
+ final boolean supportsSwitchingToNextInputMethod) {
+ final ArrayList<ImeSubtypeListItem> result = new ArrayList<>();
+ final int ALL_ITEMS_COUNT = items.size();
+ for (int i = 0; i < ALL_ITEMS_COUNT; i++) {
+ final ImeSubtypeListItem item = items.get(i);
+ if (item.mImi.supportsSwitchingToNextInputMethod() ==
+ supportsSwitchingToNextInputMethod) {
+ result.add(item);
+ }
+ }
+ return result;
+ }
+ }
+
+ private final InputMethodSettings mSettings;
+ private InputMethodAndSubtypeList mSubtypeList;
+ private ControllerImpl mController;
+
private InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context) {
mSettings = settings;
resetCircularListLocked(context);
@@ -269,19 +412,30 @@
return new InputMethodSubtypeSwitchingController(settings, context);
}
- // TODO: write unit tests for this method and the logic that determines the next subtype
public void onCommitTextLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
- // TODO: Implement this.
+ if (mController == null) {
+ if (DEBUG) {
+ Log.e(TAG, "mController shouldn't be null.");
+ }
+ return;
+ }
+ mController.onUserActionLocked(imi, subtype);
}
public void resetCircularListLocked(Context context) {
mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
+ mController = new ControllerImpl(mSubtypeList.getSortedInputMethodAndSubtypeList());
}
- public ImeSubtypeListItem getNextInputMethodLocked(
- boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
- return getNextInputMethodLockedImpl(mSubtypeList.getSortedInputMethodAndSubtypeList(),
- onlyCurrentIme, imi, subtype);
+ public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
+ InputMethodSubtype subtype) {
+ if (mController == null) {
+ if (DEBUG) {
+ Log.e(TAG, "mController shouldn't be null.");
+ }
+ return null;
+ }
+ return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
}
public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(boolean showSubtypes,
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 24e55e4..ed9f9bc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2342,7 +2342,13 @@
// Only care about partial wake locks, since full wake locks
// will be canceled when the user puts the screen to sleep.
aggregateLastWakeupUptimeLocked(uptime);
- historyName = historyName == null || mRecordAllWakeLocks ? name : historyName;
+ if (mRecordAllWakeLocks) {
+ if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, name, uid, 0)) {
+ addHistoryEventLocked(elapsedRealtime, uptime,
+ HistoryItem.EVENT_WAKE_LOCK_START, name, uid);
+ }
+ }
+ historyName = historyName == null ? name : historyName;
if (mWakeLockNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
@@ -2352,7 +2358,7 @@
mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
mWakeLockImportant = !unimportantForLogging;
addHistoryRecordLocked(elapsedRealtime, uptime);
- } else if (!mRecordAllWakeLocks && !mWakeLockImportant && !unimportantForLogging) {
+ } else if (!mWakeLockImportant && !unimportantForLogging) {
if (mHistoryLastWritten.wakelockTag != null) {
// We'll try to update the last tag.
mHistoryLastWritten.wakelockTag = null;
@@ -2362,14 +2368,6 @@
addHistoryRecordLocked(elapsedRealtime, uptime);
}
mWakeLockImportant = true;
- } else if (mRecordAllWakeLocks) {
- if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName,
- uid, 0)) {
- mWakeLockNesting++;
- return;
- }
- addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_WAKE_LOCK_START,
- historyName, uid);
}
mWakeLockNesting++;
}
@@ -2387,28 +2385,19 @@
uid = mapUid(uid);
if (type == WAKE_TYPE_PARTIAL) {
mWakeLockNesting--;
- historyName = historyName == null || mRecordAllWakeLocks ? name : historyName;
+ if (mRecordAllWakeLocks) {
+ if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, name, uid, 0)) {
+ addHistoryEventLocked(elapsedRealtime, uptime,
+ HistoryItem.EVENT_WAKE_LOCK_FINISH, name, uid);
+ }
+ }
if (mWakeLockNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
+ Integer.toHexString(mHistoryCur.states));
- if (mRecordAllWakeLocks
- || (historyName != null && !historyName.equals(mInitialAcquireWakeName))
- || uid != mInitialAcquireWakeUid) {
- mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
- mHistoryCur.wakelockTag.string = historyName;
- mHistoryCur.wakelockTag.uid = uid;
- }
mInitialAcquireWakeName = null;
mInitialAcquireWakeUid = -1;
addHistoryRecordLocked(elapsedRealtime, uptime);
- } else if (mRecordAllWakeLocks) {
- if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName,
- uid, 0)) {
- return;
- }
- addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_WAKE_LOCK_FINISH,
- historyName, uid);
}
}
if (uid >= 0) {
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index 5b59599..dca9921 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -220,31 +220,77 @@
* @see #readMapXml
*/
public static final void writeMapXml(Map val, String name, XmlSerializer out)
- throws XmlPullParserException, java.io.IOException
- {
+ throws XmlPullParserException, java.io.IOException {
+ writeMapXml(val, name, out, null);
+ }
+
+ /**
+ * Flatten a Map into an XmlSerializer. The map can later be read back
+ * with readThisMapXml().
+ *
+ * @param val The map to be flattened.
+ * @param name Name attribute to include with this list's tag, or null for
+ * none.
+ * @param out XmlSerializer to write the map into.
+ * @param callback Method to call when an Object type is not recognized.
+ *
+ * @see #writeMapXml(Map, OutputStream)
+ * @see #writeListXml
+ * @see #writeValueXml
+ * @see #readMapXml
+ *
+ * @hide
+ */
+ public static final void writeMapXml(Map val, String name, XmlSerializer out,
+ WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
+
if (val == null) {
out.startTag(null, "null");
out.endTag(null, "null");
return;
}
- Set s = val.entrySet();
- Iterator i = s.iterator();
-
out.startTag(null, "map");
if (name != null) {
out.attribute(null, "name", name);
}
- while (i.hasNext()) {
- Map.Entry e = (Map.Entry)i.next();
- writeValueXml(e.getValue(), (String)e.getKey(), out);
- }
+ writeMapXml(val, out, callback);
out.endTag(null, "map");
}
/**
+ * Flatten a Map into an XmlSerializer. The map can later be read back
+ * with readThisMapXml(). This method presumes that the start tag and
+ * name attribute have already been written and does not write an end tag.
+ *
+ * @param val The map to be flattened.
+ * @param out XmlSerializer to write the map into.
+ *
+ * @see #writeMapXml(Map, OutputStream)
+ * @see #writeListXml
+ * @see #writeValueXml
+ * @see #readMapXml
+ *
+ * @hide
+ */
+ public static final void writeMapXml(Map val, XmlSerializer out,
+ WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
+ if (val == null) {
+ return;
+ }
+
+ Set s = val.entrySet();
+ Iterator i = s.iterator();
+
+ while (i.hasNext()) {
+ Map.Entry e = (Map.Entry)i.next();
+ writeValueXml(e.getValue(), (String)e.getKey(), out, callback);
+ }
+ }
+
+ /**
* Flatten a List into an XmlSerializer. The list can later be read back
* with readThisListXml().
*
@@ -387,6 +433,123 @@
}
/**
+ * Flatten a long[] into an XmlSerializer. The list can later be read back
+ * with readThisLongArrayXml().
+ *
+ * @param val The long array to be flattened.
+ * @param name Name attribute to include with this array's tag, or null for
+ * none.
+ * @param out XmlSerializer to write the array into.
+ *
+ * @see #writeMapXml
+ * @see #writeValueXml
+ * @see #readThisIntArrayXml
+ */
+ public static final void writeLongArrayXml(long[] val, String name, XmlSerializer out)
+ throws XmlPullParserException, java.io.IOException {
+
+ if (val == null) {
+ out.startTag(null, "null");
+ out.endTag(null, "null");
+ return;
+ }
+
+ out.startTag(null, "long-array");
+ if (name != null) {
+ out.attribute(null, "name", name);
+ }
+
+ final int N = val.length;
+ out.attribute(null, "num", Integer.toString(N));
+
+ for (int i=0; i<N; i++) {
+ out.startTag(null, "item");
+ out.attribute(null, "value", Long.toString(val[i]));
+ out.endTag(null, "item");
+ }
+
+ out.endTag(null, "long-array");
+ }
+
+ /**
+ * Flatten a double[] into an XmlSerializer. The list can later be read back
+ * with readThisDoubleArrayXml().
+ *
+ * @param val The double array to be flattened.
+ * @param name Name attribute to include with this array's tag, or null for
+ * none.
+ * @param out XmlSerializer to write the array into.
+ *
+ * @see #writeMapXml
+ * @see #writeValueXml
+ * @see #readThisIntArrayXml
+ */
+ public static final void writeDoubleArrayXml(double[] val, String name, XmlSerializer out)
+ throws XmlPullParserException, java.io.IOException {
+
+ if (val == null) {
+ out.startTag(null, "null");
+ out.endTag(null, "null");
+ return;
+ }
+
+ out.startTag(null, "double-array");
+ if (name != null) {
+ out.attribute(null, "name", name);
+ }
+
+ final int N = val.length;
+ out.attribute(null, "num", Integer.toString(N));
+
+ for (int i=0; i<N; i++) {
+ out.startTag(null, "item");
+ out.attribute(null, "value", Double.toString(val[i]));
+ out.endTag(null, "item");
+ }
+
+ out.endTag(null, "double-array");
+ }
+
+ /**
+ * Flatten a String[] into an XmlSerializer. The list can later be read back
+ * with readThisStringArrayXml().
+ *
+ * @param val The long array to be flattened.
+ * @param name Name attribute to include with this array's tag, or null for
+ * none.
+ * @param out XmlSerializer to write the array into.
+ *
+ * @see #writeMapXml
+ * @see #writeValueXml
+ * @see #readThisIntArrayXml
+ */
+ public static final void writeStringArrayXml(String[] val, String name, XmlSerializer out)
+ throws XmlPullParserException, java.io.IOException {
+
+ if (val == null) {
+ out.startTag(null, "null");
+ out.endTag(null, "null");
+ return;
+ }
+
+ out.startTag(null, "string-array");
+ if (name != null) {
+ out.attribute(null, "name", name);
+ }
+
+ final int N = val.length;
+ out.attribute(null, "num", Integer.toString(N));
+
+ for (int i=0; i<N; i++) {
+ out.startTag(null, "item");
+ out.attribute(null, "value", val[i]);
+ out.endTag(null, "item");
+ }
+
+ out.endTag(null, "string-array");
+ }
+
+ /**
* Flatten an object's value into an XmlSerializer. The value can later
* be read back with readThisValueXml().
*
@@ -403,8 +566,29 @@
* @see #readValueXml
*/
public static final void writeValueXml(Object v, String name, XmlSerializer out)
- throws XmlPullParserException, java.io.IOException
- {
+ throws XmlPullParserException, java.io.IOException {
+ writeValueXml(v, name, out, null);
+ }
+
+ /**
+ * Flatten an object's value into an XmlSerializer. The value can later
+ * be read back with readThisValueXml().
+ *
+ * Currently supported value types are: null, String, Integer, Long,
+ * Float, Double Boolean, Map, List.
+ *
+ * @param v The object to be flattened.
+ * @param name Name attribute to include with this value's tag, or null
+ * for none.
+ * @param out XmlSerializer to write the object into.
+ * @param callback Handler for Object types not recognized.
+ *
+ * @see #writeMapXml
+ * @see #writeListXml
+ * @see #readValueXml
+ */
+ private static final void writeValueXml(Object v, String name, XmlSerializer out,
+ WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
String typeStr;
if (v == null) {
out.startTag(null, "null");
@@ -437,14 +621,23 @@
} else if (v instanceof int[]) {
writeIntArrayXml((int[])v, name, out);
return;
+ } else if (v instanceof long[]) {
+ writeLongArrayXml((long[])v, name, out);
+ return;
+ } else if (v instanceof double[]) {
+ writeDoubleArrayXml((double[])v, name, out);
+ return;
+ } else if (v instanceof String[]) {
+ writeStringArrayXml((String[])v, name, out);
+ return;
} else if (v instanceof Map) {
writeMapXml((Map)v, name, out);
return;
} else if (v instanceof List) {
- writeListXml((List)v, name, out);
+ writeListXml((List) v, name, out);
return;
} else if (v instanceof Set) {
- writeSetXml((Set)v, name, out);
+ writeSetXml((Set) v, name, out);
return;
} else if (v instanceof CharSequence) {
// XXX This is to allow us to at least write something if
@@ -457,6 +650,9 @@
out.text(v.toString());
out.endTag(null, "string");
return;
+ } else if (callback != null) {
+ callback.writeUnknownObject(v, name, out);
+ return;
} else {
throw new RuntimeException("writeValueXml: unable to write value " + v);
}
@@ -550,14 +746,35 @@
* @see #readMapXml
*/
public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
- String[] name) throws XmlPullParserException, java.io.IOException
+ String[] name) throws XmlPullParserException, java.io.IOException {
+ return readThisMapXml(parser, endTag, name, null);
+ }
+
+ /**
+ * Read a HashMap object from an XmlPullParser. The XML data could
+ * previously have been generated by writeMapXml(). The XmlPullParser
+ * must be positioned <em>after</em> the tag that begins the map.
+ *
+ * @param parser The XmlPullParser from which to read the map data.
+ * @param endTag Name of the tag that will end the map, usually "map".
+ * @param name An array of one string, used to return the name attribute
+ * of the map's tag.
+ *
+ * @return HashMap The newly generated map.
+ *
+ * @see #readMapXml
+ * @hide
+ */
+ public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
+ String[] name, ReadMapCallback callback)
+ throws XmlPullParserException, java.io.IOException
{
HashMap<String, Object> map = new HashMap<String, Object>();
int eventType = parser.getEventType();
do {
if (eventType == parser.START_TAG) {
- Object val = readThisValueXml(parser, name);
+ Object val = readThisValueXml(parser, name, callback);
map.put(name[0], val);
} else if (eventType == parser.END_TAG) {
if (parser.getName().equals(endTag)) {
@@ -587,15 +804,34 @@
*
* @see #readListXml
*/
- public static final ArrayList readThisListXml(XmlPullParser parser, String endTag, String[] name)
- throws XmlPullParserException, java.io.IOException
- {
+ public static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
+ String[] name) throws XmlPullParserException, java.io.IOException {
+ return readThisListXml(parser, endTag, name, null);
+ }
+
+ /**
+ * Read an ArrayList object from an XmlPullParser. The XML data could
+ * previously have been generated by writeListXml(). The XmlPullParser
+ * must be positioned <em>after</em> the tag that begins the list.
+ *
+ * @param parser The XmlPullParser from which to read the list data.
+ * @param endTag Name of the tag that will end the list, usually "list".
+ * @param name An array of one string, used to return the name attribute
+ * of the list's tag.
+ *
+ * @return HashMap The newly generated list.
+ *
+ * @see #readListXml
+ */
+ private static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
+ String[] name, ReadMapCallback callback)
+ throws XmlPullParserException, java.io.IOException {
ArrayList list = new ArrayList();
int eventType = parser.getEventType();
do {
if (eventType == parser.START_TAG) {
- Object val = readThisValueXml(parser, name);
+ Object val = readThisValueXml(parser, name, callback);
list.add(val);
//System.out.println("Adding to list: " + val);
} else if (eventType == parser.END_TAG) {
@@ -611,7 +847,29 @@
throw new XmlPullParserException(
"Document ended before " + endTag + " end tag");
}
-
+
+ /**
+ * Read a HashSet object from an XmlPullParser. The XML data could previously
+ * have been generated by writeSetXml(). The XmlPullParser must be positioned
+ * <em>after</em> the tag that begins the set.
+ *
+ * @param parser The XmlPullParser from which to read the set data.
+ * @param endTag Name of the tag that will end the set, usually "set".
+ * @param name An array of one string, used to return the name attribute
+ * of the set's tag.
+ *
+ * @return HashSet The newly generated set.
+ *
+ * @throws XmlPullParserException
+ * @throws java.io.IOException
+ *
+ * @see #readSetXml
+ */
+ public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name)
+ throws XmlPullParserException, java.io.IOException {
+ return readThisSetXml(parser, endTag, name, null);
+ }
+
/**
* Read a HashSet object from an XmlPullParser. The XML data could previously
* have been generated by writeSetXml(). The XmlPullParser must be positioned
@@ -628,15 +886,16 @@
* @throws java.io.IOException
*
* @see #readSetXml
+ * @hide
*/
- public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name)
- throws XmlPullParserException, java.io.IOException {
+ private static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name,
+ ReadMapCallback callback) throws XmlPullParserException, java.io.IOException {
HashSet set = new HashSet();
int eventType = parser.getEventType();
do {
if (eventType == parser.START_TAG) {
- Object val = readThisValueXml(parser, name);
+ Object val = readThisValueXml(parser, name, callback);
set.add(val);
//System.out.println("Adding to set: " + val);
} else if (eventType == parser.END_TAG) {
@@ -681,6 +940,7 @@
throw new XmlPullParserException(
"Not a number in num attribute in byte-array");
}
+ parser.next();
int[] array = new int[num];
int i = 0;
@@ -722,6 +982,187 @@
}
/**
+ * Read a long[] object from an XmlPullParser. The XML data could
+ * previously have been generated by writeLongArrayXml(). The XmlPullParser
+ * must be positioned <em>after</em> the tag that begins the list.
+ *
+ * @param parser The XmlPullParser from which to read the list data.
+ * @param endTag Name of the tag that will end the list, usually "list".
+ * @param name An array of one string, used to return the name attribute
+ * of the list's tag.
+ *
+ * @return Returns a newly generated long[].
+ *
+ * @see #readListXml
+ */
+ public static final long[] readThisLongArrayXml(XmlPullParser parser,
+ String endTag, String[] name)
+ throws XmlPullParserException, java.io.IOException {
+
+ int num;
+ try {
+ num = Integer.parseInt(parser.getAttributeValue(null, "num"));
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need num attribute in long-array");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Not a number in num attribute in long-array");
+ }
+ parser.next();
+
+ long[] array = new long[num];
+ int i = 0;
+
+ int eventType = parser.getEventType();
+ do {
+ if (eventType == parser.START_TAG) {
+ if (parser.getName().equals("item")) {
+ try {
+ array[i] = Long.parseLong(parser.getAttributeValue(null, "value"));
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need value attribute in item");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Not a number in value attribute in item");
+ }
+ } else {
+ throw new XmlPullParserException("Expected item tag at: " + parser.getName());
+ }
+ } else if (eventType == parser.END_TAG) {
+ if (parser.getName().equals(endTag)) {
+ return array;
+ } else if (parser.getName().equals("item")) {
+ i++;
+ } else {
+ throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
+ parser.getName());
+ }
+ }
+ eventType = parser.next();
+ } while (eventType != parser.END_DOCUMENT);
+
+ throw new XmlPullParserException("Document ended before " + endTag + " end tag");
+ }
+
+ /**
+ * Read a double[] object from an XmlPullParser. The XML data could
+ * previously have been generated by writeDoubleArrayXml(). The XmlPullParser
+ * must be positioned <em>after</em> the tag that begins the list.
+ *
+ * @param parser The XmlPullParser from which to read the list data.
+ * @param endTag Name of the tag that will end the list, usually "double-array".
+ * @param name An array of one string, used to return the name attribute
+ * of the list's tag.
+ *
+ * @return Returns a newly generated double[].
+ *
+ * @see #readListXml
+ */
+ public static final double[] readThisDoubleArrayXml(XmlPullParser parser, String endTag,
+ String[] name) throws XmlPullParserException, java.io.IOException {
+
+ int num;
+ try {
+ num = Integer.parseInt(parser.getAttributeValue(null, "num"));
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need num attribute in double-array");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Not a number in num attribute in double-array");
+ }
+ parser.next();
+
+ double[] array = new double[num];
+ int i = 0;
+
+ int eventType = parser.getEventType();
+ do {
+ if (eventType == parser.START_TAG) {
+ if (parser.getName().equals("item")) {
+ try {
+ array[i] = Double.parseDouble(parser.getAttributeValue(null, "value"));
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need value attribute in item");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Not a number in value attribute in item");
+ }
+ } else {
+ throw new XmlPullParserException("Expected item tag at: " + parser.getName());
+ }
+ } else if (eventType == parser.END_TAG) {
+ if (parser.getName().equals(endTag)) {
+ return array;
+ } else if (parser.getName().equals("item")) {
+ i++;
+ } else {
+ throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
+ parser.getName());
+ }
+ }
+ eventType = parser.next();
+ } while (eventType != parser.END_DOCUMENT);
+
+ throw new XmlPullParserException("Document ended before " + endTag + " end tag");
+ }
+
+ /**
+ * Read a String[] object from an XmlPullParser. The XML data could
+ * previously have been generated by writeStringArrayXml(). The XmlPullParser
+ * must be positioned <em>after</em> the tag that begins the list.
+ *
+ * @param parser The XmlPullParser from which to read the list data.
+ * @param endTag Name of the tag that will end the list, usually "string-array".
+ * @param name An array of one string, used to return the name attribute
+ * of the list's tag.
+ *
+ * @return Returns a newly generated String[].
+ *
+ * @see #readListXml
+ */
+ public static final String[] readThisStringArrayXml(XmlPullParser parser, String endTag,
+ String[] name) throws XmlPullParserException, java.io.IOException {
+
+ int num;
+ try {
+ num = Integer.parseInt(parser.getAttributeValue(null, "num"));
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need num attribute in string-array");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Not a number in num attribute in string-array");
+ }
+ parser.next();
+
+ String[] array = new String[num];
+ int i = 0;
+
+ int eventType = parser.getEventType();
+ do {
+ if (eventType == parser.START_TAG) {
+ if (parser.getName().equals("item")) {
+ try {
+ array[i] = parser.getAttributeValue(null, "value");
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need value attribute in item");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Not a number in value attribute in item");
+ }
+ } else {
+ throw new XmlPullParserException("Expected item tag at: " + parser.getName());
+ }
+ } else if (eventType == parser.END_TAG) {
+ if (parser.getName().equals(endTag)) {
+ return array;
+ } else if (parser.getName().equals("item")) {
+ i++;
+ } else {
+ throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
+ parser.getName());
+ }
+ }
+ eventType = parser.next();
+ } while (eventType != parser.END_DOCUMENT);
+
+ throw new XmlPullParserException("Document ended before " + endTag + " end tag");
+ }
+
+ /**
* Read a flattened object from an XmlPullParser. The XML data could
* previously have been written with writeMapXml(), writeListXml(), or
* writeValueXml(). The XmlPullParser must be positioned <em>at</em> the
@@ -743,7 +1184,7 @@
int eventType = parser.getEventType();
do {
if (eventType == parser.START_TAG) {
- return readThisValueXml(parser, name);
+ return readThisValueXml(parser, name, null);
} else if (eventType == parser.END_TAG) {
throw new XmlPullParserException(
"Unexpected end tag at: " + parser.getName());
@@ -758,9 +1199,8 @@
"Unexpected end of document");
}
- private static final Object readThisValueXml(XmlPullParser parser, String[] name)
- throws XmlPullParserException, java.io.IOException
- {
+ private static final Object readThisValueXml(XmlPullParser parser, String[] name,
+ ReadMapCallback callback) throws XmlPullParserException, java.io.IOException {
final String valueName = parser.getAttributeValue(null, "name");
final String tagName = parser.getName();
@@ -794,11 +1234,25 @@
} else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
// all work already done by readThisPrimitiveValueXml
} else if (tagName.equals("int-array")) {
- parser.next();
res = readThisIntArrayXml(parser, "int-array", name);
name[0] = valueName;
//System.out.println("Returning value for " + valueName + ": " + res);
return res;
+ } else if (tagName.equals("long-array")) {
+ res = readThisLongArrayXml(parser, "long-array", name);
+ name[0] = valueName;
+ //System.out.println("Returning value for " + valueName + ": " + res);
+ return res;
+ } else if (tagName.equals("double-array")) {
+ res = readThisDoubleArrayXml(parser, "double-array", name);
+ name[0] = valueName;
+ //System.out.println("Returning value for " + valueName + ": " + res);
+ return res;
+ } else if (tagName.equals("string-array")) {
+ res = readThisStringArrayXml(parser, "string-array", name);
+ name[0] = valueName;
+ //System.out.println("Returning value for " + valueName + ": " + res);
+ return res;
} else if (tagName.equals("map")) {
parser.next();
res = readThisMapXml(parser, "map", name);
@@ -817,9 +1271,12 @@
name[0] = valueName;
//System.out.println("Returning value for " + valueName + ": " + res);
return res;
+ } else if (callback != null) {
+ res = callback.readThisUnknownObjectXml(parser, tagName);
+ name[0] = valueName;
+ return res;
} else {
- throw new XmlPullParserException(
- "Unknown tag: " + tagName);
+ throw new XmlPullParserException("Unknown tag: " + tagName);
}
// Skip through to end tag.
@@ -967,4 +1424,39 @@
throws IOException {
out.attribute(null, name, Boolean.toString(value));
}
+
+ /** @hide */
+ public interface WriteMapCallback {
+ /**
+ * Called from writeMapXml when an Object type is not recognized. The implementer
+ * must write out the entire element including start and end tags.
+ *
+ * @param v The object to be written out
+ * @param name The mapping key for v. Must be written into the "name" attribute of the
+ * start tag.
+ * @param out The XML output stream.
+ * @throws XmlPullParserException on unrecognized Object type.
+ * @throws IOException on XmlSerializer serialization errors.
+ * @hide
+ */
+ public void writeUnknownObject(Object v, String name, XmlSerializer out)
+ throws XmlPullParserException, IOException;
+ }
+
+ /** @hide */
+ public interface ReadMapCallback {
+ /**
+ * Called from readThisMapXml when a START_TAG is not recognized. The input stream
+ * is positioned within the start tag so that attributes can be read using in.getAttribute.
+ *
+ * @param in the XML input stream
+ * @param tag the START_TAG that was not recognized.
+ * @return the Object parsed from the stream which will be put into the map.
+ * @throws XmlPullParserException if the START_TAG is not recognized.
+ * @throws IOException on XmlPullParser serialization errors.
+ * @hide
+ */
+ public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
+ throws XmlPullParserException, IOException;
+ }
}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 9501f92..c70841b 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -16,6 +16,8 @@
package com.android.internal.widget;
+import com.android.internal.widget.ILockSettingsObserver;
+
/** {@hide} */
interface ILockSettings {
void setBoolean(in String key, in boolean value, in int userId);
@@ -32,4 +34,6 @@
boolean havePattern(int userId);
boolean havePassword(int userId);
void removeUser(int userId);
+ void registerObserver(in ILockSettingsObserver observer);
+ void unregisterObserver(in ILockSettingsObserver observer);
}
diff --git a/core/java/com/android/internal/widget/ILockSettingsObserver.aidl b/core/java/com/android/internal/widget/ILockSettingsObserver.aidl
new file mode 100644
index 0000000..6c354d8
--- /dev/null
+++ b/core/java/com/android/internal/widget/ILockSettingsObserver.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.internal.widget;
+
+/** {@hide} */
+oneway interface ILockSettingsObserver {
+ void onLockSettingChanged(in String key, in int userId);
+}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 2882b54..25e3463 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -199,8 +199,8 @@
private ILockSettings getLockSettings() {
if (mLockSettingsService == null) {
- mLockSettingsService = ILockSettings.Stub.asInterface(
- (IBinder) ServiceManager.getService("lock_settings"));
+ mLockSettingsService = LockPatternUtilsCache.getInstance(
+ ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings")));
}
return mLockSettingsService;
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtilsCache.java b/core/java/com/android/internal/widget/LockPatternUtilsCache.java
new file mode 100644
index 0000000..550aa6d
--- /dev/null
+++ b/core/java/com/android/internal/widget/LockPatternUtilsCache.java
@@ -0,0 +1,222 @@
+/*
+ * 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.internal.widget;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+
+/**
+ * A decorator for {@link ILockSettings} that caches the key-value responses in memory.
+ *
+ * Specifically, the return values of {@link #getString(String, String, int)},
+ * {@link #getLong(String, long, int)} and {@link #getBoolean(String, boolean, int)} are cached.
+ */
+public class LockPatternUtilsCache implements ILockSettings {
+
+ private static LockPatternUtilsCache sInstance;
+
+ private final ILockSettings mService;
+
+ /** Only access when holding {@code mCache} lock. */
+ private final ArrayMap<CacheKey, Object> mCache = new ArrayMap<>();
+
+ /** Only access when holding {@link #mCache} lock. */
+ private final CacheKey mCacheKey = new CacheKey();
+
+
+ public static synchronized LockPatternUtilsCache getInstance(ILockSettings service) {
+ if (sInstance == null) {
+ sInstance = new LockPatternUtilsCache(service);
+ }
+ return sInstance;
+ }
+
+ // ILockSettings
+
+ private LockPatternUtilsCache(ILockSettings service) {
+ mService = service;
+ try {
+ service.registerObserver(mObserver);
+ } catch (RemoteException e) {
+ // Not safe to do caching without the observer. System process has probably died
+ // anyway, so crashing here is fine.
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void setBoolean(String key, boolean value, int userId) throws RemoteException {
+ invalidateCache(key, userId);
+ mService.setBoolean(key, value, userId);
+ putCache(key, userId, value);
+ }
+
+ public void setLong(String key, long value, int userId) throws RemoteException {
+ invalidateCache(key, userId);
+ mService.setLong(key, value, userId);
+ putCache(key, userId, value);
+ }
+
+ public void setString(String key, String value, int userId) throws RemoteException {
+ invalidateCache(key, userId);
+ mService.setString(key, value, userId);
+ putCache(key, userId, value);
+ }
+
+ public long getLong(String key, long defaultValue, int userId) throws RemoteException {
+ Object value = peekCache(key, userId);
+ if (value instanceof Long) {
+ return (long) value;
+ }
+ long result = mService.getLong(key, defaultValue, userId);
+ putCache(key, userId, result);
+ return result;
+ }
+
+ public String getString(String key, String defaultValue, int userId) throws RemoteException {
+ Object value = peekCache(key, userId);
+ if (value instanceof String) {
+ return (String) value;
+ }
+ String result = mService.getString(key, defaultValue, userId);
+ putCache(key, userId, result);
+ return result;
+ }
+
+ public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
+ Object value = peekCache(key, userId);
+ if (value instanceof Boolean) {
+ return (boolean) value;
+ }
+ boolean result = mService.getBoolean(key, defaultValue, userId);
+ putCache(key, userId, result);
+ return result;
+ }
+
+ @Override
+ public void setLockPattern(String pattern, int userId) throws RemoteException {
+ mService.setLockPattern(pattern, userId);
+ }
+
+ @Override
+ public boolean checkPattern(String pattern, int userId) throws RemoteException {
+ return mService.checkPattern(pattern, userId);
+ }
+
+ @Override
+ public void setLockPassword(String password, int userId) throws RemoteException {
+ mService.setLockPassword(password, userId);
+ }
+
+ @Override
+ public boolean checkPassword(String password, int userId) throws RemoteException {
+ return mService.checkPassword(password, userId);
+ }
+
+ @Override
+ public boolean checkVoldPassword(int userId) throws RemoteException {
+ return mService.checkVoldPassword(userId);
+ }
+
+ @Override
+ public boolean havePattern(int userId) throws RemoteException {
+ return mService.havePattern(userId);
+ }
+
+ @Override
+ public boolean havePassword(int userId) throws RemoteException {
+ return mService.havePassword(userId);
+ }
+
+ @Override
+ public void removeUser(int userId) throws RemoteException {
+ mService.removeUser(userId);
+ }
+
+ @Override
+ public void registerObserver(ILockSettingsObserver observer) throws RemoteException {
+ mService.registerObserver(observer);
+ }
+
+ @Override
+ public void unregisterObserver(ILockSettingsObserver observer) throws RemoteException {
+ mService.unregisterObserver(observer);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mService.asBinder();
+ }
+
+ // Caching
+
+ private Object peekCache(String key, int userId) {
+ synchronized (mCache) {
+ // Safe to reuse mCacheKey, because it is not stored in the map.
+ return mCache.get(mCacheKey.set(key, userId));
+ }
+ }
+
+ private void putCache(String key, int userId, Object value) {
+ synchronized (mCache) {
+ // Create a new key, because this will be stored in the map.
+ mCache.put(new CacheKey().set(key, userId), value);
+ }
+ }
+
+ private void invalidateCache(String key, int userId) {
+ synchronized (mCache) {
+ // Safe to reuse mCacheKey, because it is not stored in the map.
+ mCache.remove(mCacheKey.set(key, userId));
+ }
+ }
+
+ private final ILockSettingsObserver mObserver = new ILockSettingsObserver.Stub() {
+ @Override
+ public void onLockSettingChanged(String key, int userId) throws RemoteException {
+ invalidateCache(key, userId);
+ }
+ };
+
+ private static final class CacheKey {
+ String key;
+ int userId;
+
+ public CacheKey set(String key, int userId) {
+ this.key = key;
+ this.userId = userId;
+ return this;
+ }
+
+ public CacheKey copy() {
+ return new CacheKey().set(key, userId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CacheKey))
+ return false;
+ CacheKey o = (CacheKey) obj;
+ return userId == o.userId && key.equals(o.key);
+ }
+
+ @Override
+ public int hashCode() {
+ return key.hashCode() ^ userId;
+ }
+ }
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 2d72494..f446c3a 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -138,6 +138,7 @@
android_hardware_Camera.cpp \
android_hardware_camera2_CameraMetadata.cpp \
android_hardware_camera2_legacy_LegacyCameraDevice.cpp \
+ android_hardware_camera2_DngCreator.cpp \
android_hardware_SensorManager.cpp \
android_hardware_SerialPort.cpp \
android_hardware_UsbDevice.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 2d350e0..0c7eefa 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -80,6 +80,7 @@
extern int register_android_hardware_Camera(JNIEnv *env);
extern int register_android_hardware_camera2_CameraMetadata(JNIEnv *env);
extern int register_android_hardware_camera2_legacy_LegacyCameraDevice(JNIEnv *env);
+extern int register_android_hardware_camera2_DngCreator(JNIEnv *env);
extern int register_android_hardware_SensorManager(JNIEnv *env);
extern int register_android_hardware_SerialPort(JNIEnv *env);
extern int register_android_hardware_UsbDevice(JNIEnv *env);
@@ -1286,6 +1287,7 @@
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
+ REG_JNI(register_android_hardware_camera2_DngCreator),
REG_JNI(register_android_hardware_SensorManager),
REG_JNI(register_android_hardware_SerialPort),
REG_JNI(register_android_hardware_UsbDevice),
diff --git a/core/jni/android/graphics/DrawFilter.cpp b/core/jni/android/graphics/DrawFilter.cpp
index fbfa2ec..3275875 100644
--- a/core/jni/android/graphics/DrawFilter.cpp
+++ b/core/jni/android/graphics/DrawFilter.cpp
@@ -30,6 +30,35 @@
namespace android {
+// Custom version of SkPaintFlagsDrawFilter that also calls setFilterLevel.
+class CompatFlagsDrawFilter : public SkPaintFlagsDrawFilter {
+public:
+ CompatFlagsDrawFilter(uint32_t clearFlags, uint32_t setFlags,
+ SkPaint::FilterLevel desiredLevel)
+ : SkPaintFlagsDrawFilter(clearFlags, setFlags)
+ , fDesiredLevel(desiredLevel) {
+ }
+
+ virtual bool filter(SkPaint* paint, Type type) {
+ SkPaintFlagsDrawFilter::filter(paint, type);
+ paint->setFilterLevel(fDesiredLevel);
+ return true;
+ }
+
+private:
+ const SkPaint::FilterLevel fDesiredLevel;
+};
+
+// Returns whether flags contains FILTER_BITMAP_FLAG. If flags does, remove it.
+static inline bool hadFiltering(jint& flags) {
+ // Equivalent to the Java Paint's FILTER_BITMAP_FLAG.
+ static const uint32_t sFilterBitmapFlag = 0x02;
+
+ const bool result = (flags & sFilterBitmapFlag) != 0;
+ flags &= ~sFilterBitmapFlag;
+ return result;
+}
+
class SkDrawFilterGlue {
public:
@@ -40,12 +69,25 @@
static jlong CreatePaintFlagsDF(JNIEnv* env, jobject clazz,
jint clearFlags, jint setFlags) {
- // trim off any out-of-range bits
- clearFlags &= SkPaint::kAllFlags;
- setFlags &= SkPaint::kAllFlags;
-
if (clearFlags | setFlags) {
- SkDrawFilter* filter = new SkPaintFlagsDrawFilter(clearFlags, setFlags);
+ // Mask both groups of flags to remove FILTER_BITMAP_FLAG, which no
+ // longer has a Skia equivalent flag (instead it corresponds to
+ // calling setFilterLevel), and keep track of which group(s), if
+ // any, had the flag set.
+ const bool turnFilteringOn = hadFiltering(setFlags);
+ const bool turnFilteringOff = hadFiltering(clearFlags);
+
+ SkDrawFilter* filter;
+ if (turnFilteringOn) {
+ // Turning filtering on overrides turning it off.
+ filter = new CompatFlagsDrawFilter(clearFlags, setFlags,
+ SkPaint::kLow_FilterLevel);
+ } else if (turnFilteringOff) {
+ filter = new CompatFlagsDrawFilter(clearFlags, setFlags,
+ SkPaint::kNone_FilterLevel);
+ } else {
+ filter = new SkPaintFlagsDrawFilter(clearFlags, setFlags);
+ }
return reinterpret_cast<jlong>(filter);
} else {
return NULL;
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index b389d9e..0cfcaef 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -50,26 +50,16 @@
///////////////////////////////////////////////////////////////////////////////////////////////
-static void Shader_destructor(JNIEnv* env, jobject o, jlong shaderHandle, jlong skiaShaderHandle)
+static void Shader_destructor(JNIEnv* env, jobject o, jlong shaderHandle)
{
SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
- SkiaShader* skiaShader = reinterpret_cast<SkiaShader*>(skiaShaderHandle);
SkSafeUnref(shader);
- // skiaShader == NULL when not !USE_OPENGL_RENDERER, so no need to delete it outside the ifdef
-#ifdef USE_OPENGL_RENDERER
- if (android::uirenderer::Caches::hasInstance()) {
- android::uirenderer::Caches::getInstance().resourceCache.destructor(skiaShader);
- } else {
- delete skiaShader;
- }
-#endif
}
static void Shader_setLocalMatrix(JNIEnv* env, jobject o, jlong shaderHandle,
- jlong skiaShaderHandle, jlong matrixHandle)
+ jlong matrixHandle)
{
SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
- SkiaShader* skiaShader = reinterpret_cast<SkiaShader*>(skiaShaderHandle);
const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
if (shader) {
if (NULL == matrix) {
@@ -78,9 +68,6 @@
else {
shader->setLocalMatrix(*matrix);
}
-#ifdef USE_OPENGL_RENDERER
- skiaShader->setMatrix(const_cast<SkMatrix*>(matrix));
-#endif
}
}
@@ -98,20 +85,6 @@
return reinterpret_cast<jlong>(s);
}
-static jlong BitmapShader_postConstructor(JNIEnv* env, jobject o, jlong shaderHandle,
- jlong bitmapHandle, jint tileModeX, jint tileModeY) {
- SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-#ifdef USE_OPENGL_RENDERER
- SkiaShader* skiaShader = new SkiaBitmapShader(bitmap, shader,
- static_cast<SkShader::TileMode>(tileModeX), static_cast<SkShader::TileMode>(tileModeY),
- NULL, !shader->isOpaque());
- return reinterpret_cast<jlong>(skiaShader);
-#else
- return NULL;
-#endif
-}
-
///////////////////////////////////////////////////////////////////////////////////////////////
static jlong LinearGradient_create1(JNIEnv* env, jobject o,
@@ -141,105 +114,6 @@
return reinterpret_cast<jlong>(shader);
}
-static jlong LinearGradient_postCreate1(JNIEnv* env, jobject o, jlong shaderHandle,
- jfloat x0, jfloat y0, jfloat x1, jfloat y1, jintArray colorArray,
- jfloatArray posArray, jint tileMode) {
-#ifdef USE_OPENGL_RENDERER
- SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
- size_t count = env->GetArrayLength(colorArray);
- const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
-
- jfloat* storedBounds = new jfloat[4];
- storedBounds[0] = x0; storedBounds[1] = y0;
- storedBounds[2] = x1; storedBounds[3] = y1;
-
- bool missFirst = false;
- bool missLast = false;
- size_t stopCount = count;
-
- jfloat* storedPositions = NULL;
- if (posArray) {
- AutoJavaFloatArray autoPos(env, posArray, count);
- const float* posValues = autoPos.ptr();
-
- missFirst = posValues[0] != 0.0f;
- missLast = posValues[count - 1] != 1.0f;
-
- stopCount += missFirst + missLast;
- storedPositions = new jfloat[stopCount];
-
- if (missFirst) {
- storedPositions[0] = 0.0f;
- }
-
- for (size_t i = missFirst; i < count + missFirst; i++) {
- storedPositions[i] = posValues[i - missFirst];
- }
-
- if (missLast) {
- storedPositions[stopCount - 1] = 1.0f;
- }
- } else {
- storedPositions = new jfloat[count];
- storedPositions[0] = 0.0f;
- const jfloat step = 1.0f / (count - 1);
- for (size_t i = 1; i < count - 1; i++) {
- storedPositions[i] = step * i;
- }
- storedPositions[count - 1] = 1.0f;
- }
-
- uint32_t* storedColors = new uint32_t[stopCount];
-
- if (missFirst) {
- storedColors[0] = static_cast<uint32_t>(colorValues[0]);
- }
-
- for (size_t i = missFirst; i < count + missFirst; i++) {
- storedColors[i] = static_cast<uint32_t>(colorValues[i - missFirst]);
- }
-
- if (missLast) {
- storedColors[stopCount - 1] = static_cast<uint32_t>(colorValues[count - 1]);
- }
-
- SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
- storedPositions, stopCount, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
- !shader->isOpaque());
-
- env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
- return reinterpret_cast<jlong>(skiaShader);
-#else
- return NULL;
-#endif
-}
-
-static jlong LinearGradient_postCreate2(JNIEnv* env, jobject o, jlong shaderHandle,
- jfloat x0, jfloat y0, jfloat x1, jfloat y1, jint color0, jint color1, jint tileMode) {
-#ifdef USE_OPENGL_RENDERER
- SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
- float* storedBounds = new float[4];
- storedBounds[0] = x0; storedBounds[1] = y0;
- storedBounds[2] = x1; storedBounds[3] = y1;
-
- float* storedPositions = new float[2];
- storedPositions[0] = 0.0f;
- storedPositions[1] = 1.0f;
-
- uint32_t* storedColors = new uint32_t[2];
- storedColors[0] = static_cast<uint32_t>(color0);
- storedColors[1] = static_cast<uint32_t>(color1);
-
- SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
- storedPositions, 2, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
- !shader->isOpaque());
-
- return reinterpret_cast<jlong>(skiaShader);
-#else
- return NULL;
-#endif
-}
-
static jlong LinearGradient_create2(JNIEnv* env, jobject o,
jfloat x0, jfloat y0, jfloat x1, jfloat y1,
jint color0, jint color1, jint tileMode)
@@ -300,67 +174,6 @@
return reinterpret_cast<jlong>(s);
}
-static jlong RadialGradient_postCreate1(JNIEnv* env, jobject o, jlong shaderHandle,
- jfloat x, jfloat y, jfloat radius, jintArray colorArray, jfloatArray posArray, jint tileMode) {
-#ifdef USE_OPENGL_RENDERER
- SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
- size_t count = env->GetArrayLength(colorArray);
- const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
-
- jfloat* storedPositions = new jfloat[count];
- uint32_t* storedColors = new uint32_t[count];
- for (size_t i = 0; i < count; i++) {
- storedColors[i] = static_cast<uint32_t>(colorValues[i]);
- }
-
- if (posArray) {
- AutoJavaFloatArray autoPos(env, posArray, count);
- const float* posValues = autoPos.ptr();
- for (size_t i = 0; i < count; i++) {
- storedPositions[i] = posValues[i];
- }
- } else {
- storedPositions[0] = 0.0f;
- const jfloat step = 1.0f / (count - 1);
- for (size_t i = 1; i < count - 1; i++) {
- storedPositions[i] = step * i;
- }
- storedPositions[count - 1] = 1.0f;
- }
-
- SkiaShader* skiaShader = new SkiaCircularGradientShader(x, y, radius, storedColors,
- storedPositions, count, shader, (SkShader::TileMode) tileMode, NULL,
- !shader->isOpaque());
-
- env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
- return reinterpret_cast<jlong>(skiaShader);
-#else
- return NULL;
-#endif
-}
-
-static jlong RadialGradient_postCreate2(JNIEnv* env, jobject o, jlong shaderHandle,
- jfloat x, jfloat y, jfloat radius, jint color0, jint color1, jint tileMode) {
-#ifdef USE_OPENGL_RENDERER
- SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
- float* storedPositions = new float[2];
- storedPositions[0] = 0.0f;
- storedPositions[1] = 1.0f;
-
- uint32_t* storedColors = new uint32_t[2];
- storedColors[0] = static_cast<uint32_t>(color0);
- storedColors[1] = static_cast<uint32_t>(color1);
-
- SkiaShader* skiaShader = new SkiaCircularGradientShader(x, y, radius, storedColors,
- storedPositions, 2, shader, (SkShader::TileMode) tileMode, NULL,
- !shader->isOpaque());
-
- return reinterpret_cast<jlong>(skiaShader);
-#else
- return NULL;
-#endif
-}
-
///////////////////////////////////////////////////////////////////////////////
static jlong SweepGradient_create1(JNIEnv* env, jobject, jfloat x, jfloat y,
@@ -393,65 +206,6 @@
return reinterpret_cast<jlong>(s);
}
-static jlong SweepGradient_postCreate1(JNIEnv* env, jobject o, jlong shaderHandle,
- jfloat x, jfloat y, jintArray colorArray, jfloatArray posArray) {
-#ifdef USE_OPENGL_RENDERER
- SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
- size_t count = env->GetArrayLength(colorArray);
- const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
-
- jfloat* storedPositions = new jfloat[count];
- uint32_t* storedColors = new uint32_t[count];
- for (size_t i = 0; i < count; i++) {
- storedColors[i] = static_cast<uint32_t>(colorValues[i]);
- }
-
- if (posArray) {
- AutoJavaFloatArray autoPos(env, posArray, count);
- const float* posValues = autoPos.ptr();
- for (size_t i = 0; i < count; i++) {
- storedPositions[i] = posValues[i];
- }
- } else {
- storedPositions[0] = 0.0f;
- const jfloat step = 1.0f / (count - 1);
- for (size_t i = 1; i < count - 1; i++) {
- storedPositions[i] = step * i;
- }
- storedPositions[count - 1] = 1.0f;
- }
-
- SkiaShader* skiaShader = new SkiaSweepGradientShader(x, y, storedColors, storedPositions, count,
- shader, NULL, !shader->isOpaque());
-
- env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
- return reinterpret_cast<jlong>(skiaShader);
-#else
- return NULL;
-#endif
-}
-
-static jlong SweepGradient_postCreate2(JNIEnv* env, jobject o, jlong shaderHandle,
- jfloat x, jfloat y, jint color0, jint color1) {
-#ifdef USE_OPENGL_RENDERER
- SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
- float* storedPositions = new float[2];
- storedPositions[0] = 0.0f;
- storedPositions[1] = 1.0f;
-
- uint32_t* storedColors = new uint32_t[2];
- storedColors[0] = static_cast<uint32_t>(color0);
- storedColors[1] = static_cast<uint32_t>(color1);
-
- SkiaShader* skiaShader = new SkiaSweepGradientShader(x, y, storedColors, storedPositions, 2,
- shader, NULL, !shader->isOpaque());
-
- return reinterpret_cast<jlong>(skiaShader);
-#else
- return NULL;
-#endif
-}
-
///////////////////////////////////////////////////////////////////////////////////////////////
static jlong ComposeShader_create1(JNIEnv* env, jobject o,
@@ -476,40 +230,6 @@
return reinterpret_cast<jlong>(shader);
}
-static jlong ComposeShader_postCreate2(JNIEnv* env, jobject o, jlong shaderHandle,
- jlong shaderAHandle, jlong shaderBHandle, jint porterDuffModeHandle) {
-#ifdef USE_OPENGL_RENDERER
- SkShader* shader = reinterpret_cast<SkShader *>(shaderHandle);
- SkiaShader* shaderA = reinterpret_cast<SkiaShader *>(shaderAHandle);
- SkiaShader* shaderB = reinterpret_cast<SkiaShader *>(shaderBHandle);
- SkPorterDuff::Mode porterDuffMode = static_cast<SkPorterDuff::Mode>(porterDuffModeHandle);
- SkXfermode::Mode mode = SkPorterDuff::ToXfermodeMode(porterDuffMode);
- SkiaShader* skiaShader = new SkiaComposeShader(shaderA, shaderB, mode, shader);
- return reinterpret_cast<jlong>(skiaShader);
-#else
- return NULL;
-#endif
-}
-
-static jlong ComposeShader_postCreate1(JNIEnv* env, jobject o, jlong shaderHandle,
- jlong shaderAHandle, jlong shaderBHandle, jlong modeHandle) {
-#ifdef USE_OPENGL_RENDERER
- SkShader* shader = reinterpret_cast<SkShader *>(shaderHandle);
- SkiaShader* shaderA = reinterpret_cast<SkiaShader *>(shaderAHandle);
- SkiaShader* shaderB = reinterpret_cast<SkiaShader *>(shaderBHandle);
- SkXfermode* mode = reinterpret_cast<SkXfermode *>(modeHandle);
- SkXfermode::Mode skiaMode;
- if (!SkXfermode::AsMode(mode, &skiaMode)) {
- // TODO: Support other modes
- skiaMode = SkXfermode::kSrcOver_Mode;
- }
- SkiaShader* skiaShader = new SkiaComposeShader(shaderA, shaderB, skiaMode, shader);
- return reinterpret_cast<jlong>(skiaShader);
-#else
- return NULL;
-#endif
-}
-
///////////////////////////////////////////////////////////////////////////////////////////////
static JNINativeMethod gColorMethods[] = {
@@ -518,41 +238,32 @@
};
static JNINativeMethod gShaderMethods[] = {
- { "nativeDestructor", "(JJ)V", (void*)Shader_destructor },
- { "nativeSetLocalMatrix", "(JJJ)V", (void*)Shader_setLocalMatrix }
+ { "nativeDestructor", "(J)V", (void*)Shader_destructor },
+ { "nativeSetLocalMatrix", "(JJ)V", (void*)Shader_setLocalMatrix }
};
static JNINativeMethod gBitmapShaderMethods[] = {
{ "nativeCreate", "(JII)J", (void*)BitmapShader_constructor },
- { "nativePostCreate", "(JJII)J", (void*)BitmapShader_postConstructor }
};
static JNINativeMethod gLinearGradientMethods[] = {
{ "nativeCreate1", "(FFFF[I[FI)J", (void*)LinearGradient_create1 },
{ "nativeCreate2", "(FFFFIII)J", (void*)LinearGradient_create2 },
- { "nativePostCreate1", "(JFFFF[I[FI)J", (void*)LinearGradient_postCreate1 },
- { "nativePostCreate2", "(JFFFFIII)J", (void*)LinearGradient_postCreate2 }
};
static JNINativeMethod gRadialGradientMethods[] = {
{ "nativeCreate1", "(FFF[I[FI)J", (void*)RadialGradient_create1 },
{ "nativeCreate2", "(FFFIII)J", (void*)RadialGradient_create2 },
- { "nativePostCreate1", "(JFFF[I[FI)J", (void*)RadialGradient_postCreate1 },
- { "nativePostCreate2", "(JFFFIII)J", (void*)RadialGradient_postCreate2 }
};
static JNINativeMethod gSweepGradientMethods[] = {
{ "nativeCreate1", "(FF[I[F)J", (void*)SweepGradient_create1 },
{ "nativeCreate2", "(FFII)J", (void*)SweepGradient_create2 },
- { "nativePostCreate1", "(JFF[I[F)J", (void*)SweepGradient_postCreate1 },
- { "nativePostCreate2", "(JFFII)J", (void*)SweepGradient_postCreate2 }
};
static JNINativeMethod gComposeShaderMethods[] = {
{ "nativeCreate1", "(JJJ)J", (void*)ComposeShader_create1 },
{ "nativeCreate2", "(JJI)J", (void*)ComposeShader_create2 },
- { "nativePostCreate1", "(JJJJ)J", (void*)ComposeShader_postCreate1 },
- { "nativePostCreate2", "(JJJI)J", (void*)ComposeShader_postCreate2 }
};
#include <android_runtime/AndroidRuntime.h>
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index fcf8f83e..1938cf4 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -175,11 +175,26 @@
gBaseEventQueueClassInfo.dispatchFlushCompleteEvent,
buffer[i].meta_data.sensor);
} else {
+ int8_t status;
+ switch (buffer[i].type) {
+ case SENSOR_TYPE_ORIENTATION:
+ case SENSOR_TYPE_MAGNETIC_FIELD:
+ case SENSOR_TYPE_ACCELEROMETER:
+ case SENSOR_TYPE_GYROSCOPE:
+ status = buffer[i].vector.status;
+ break;
+ case SENSOR_TYPE_HEART_RATE:
+ status = buffer[i].heart_rate.status;
+ break;
+ default:
+ status = SENSOR_STATUS_ACCURACY_HIGH;
+ break;
+ }
env->CallVoidMethod(mReceiverObject,
gBaseEventQueueClassInfo.dispatchSensorEvent,
buffer[i].sensor,
mScratch,
- buffer[i].vector.status,
+ status,
buffer[i].timestamp);
}
if (env->ExceptionCheck()) {
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 0d2df80..79353299 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -39,6 +39,9 @@
#include <nativehelper/ScopedUtfChars.h>
#include <nativehelper/ScopedPrimitiveArray.h>
+#include <sys/types.h> // for socketpair
+#include <sys/socket.h> // for socketpair
+
#if defined(LOG_NNDEBUG)
#if !LOG_NNDEBUG
#define ALOGVV ALOGV
@@ -351,6 +354,119 @@
}
}
+struct DumpMetadataParams {
+ int writeFd;
+ const CameraMetadata* metadata;
+};
+
+static void* CameraMetadata_writeMetadataThread(void* arg) {
+ DumpMetadataParams* p = static_cast<DumpMetadataParams*>(arg);
+
+ /*
+ * Write the dumped data, and close the writing side FD.
+ */
+ p->metadata->dump(p->writeFd, /*verbosity*/2);
+
+ if (close(p->writeFd) < 0) {
+ ALOGE("%s: Failed to close writeFd (errno = %#x, message = '%s')",
+ __FUNCTION__, errno, strerror(errno));
+ }
+
+ return NULL;
+}
+
+static void CameraMetadata_dump(JNIEnv *env, jobject thiz) {
+ ALOGV("%s", __FUNCTION__);
+ CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
+ if (metadata == NULL) {
+ return;
+ }
+
+ /*
+ * Create a socket pair for local streaming read/writes.
+ *
+ * The metadata will be dumped into the write side,
+ * and then read back out (and logged) via the read side.
+ */
+
+ int writeFd, readFd;
+ {
+
+ int sv[2];
+ if (socketpair(AF_LOCAL, SOCK_STREAM, /*protocol*/0, &sv[0]) < 0) {
+ jniThrowExceptionFmt(env, "java/io/IOException",
+ "Failed to create socketpair (errno = %#x, message = '%s')",
+ errno, strerror(errno));
+ return;
+ }
+ writeFd = sv[0];
+ readFd = sv[1];
+ }
+
+ /*
+ * Create a thread for doing the writing.
+ *
+ * The reading and writing must be concurrent, otherwise
+ * the write will block forever once it exhausts the capped
+ * buffer size (from getsockopt).
+ */
+ pthread_t writeThread;
+ DumpMetadataParams params = {
+ writeFd,
+ metadata
+ };
+
+ {
+ int threadRet = pthread_create(&writeThread, /*attr*/NULL,
+ CameraMetadata_writeMetadataThread, (void*)¶ms);
+
+ if (threadRet != 0) {
+ close(writeFd);
+
+ jniThrowExceptionFmt(env, "java/io/IOException",
+ "Failed to create thread for writing (errno = %#x, message = '%s')",
+ threadRet, strerror(threadRet));
+ }
+ }
+
+ /*
+ * Read out a byte until stream is complete. Write completed lines
+ * to ALOG.
+ */
+ {
+ char out[] = {'\0', '\0'}; // large enough to append as a string
+ String8 logLine;
+
+ // Read one byte at a time! Very slow but avoids complicated \n scanning.
+ ssize_t res;
+ while ((res = TEMP_FAILURE_RETRY(read(readFd, &out[0], /*count*/1))) > 0) {
+ if (out[0] == '\n') {
+ ALOGD("%s", logLine.string());
+ logLine.clear();
+ } else {
+ logLine.append(out);
+ }
+ }
+
+ if (res < 0) {
+ jniThrowExceptionFmt(env, "java/io/IOException",
+ "Failed to read from fd (errno = %#x, message = '%s')",
+ errno, strerror(errno));
+ //return;
+ } else if (!logLine.isEmpty()) {
+ ALOGD("%s", logLine.string());
+ }
+ }
+
+ int res;
+
+ // Join until thread finishes. Ensures params/metadata is valid until then.
+ if ((res = pthread_join(writeThread, /*retval*/NULL)) != 0) {
+ ALOGE("%s: Failed to join thread (errno = %#x, message = '%s')",
+ __FUNCTION__, res, strerror(res));
+ }
+}
+
static void CameraMetadata_readFromParcel(JNIEnv *env, jobject thiz, jobject parcel) {
ALOGV("%s", __FUNCTION__);
CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
@@ -436,6 +552,9 @@
{ "nativeWriteValues",
"(I[B)V",
(void *)CameraMetadata_writeValues },
+ { "nativeDump",
+ "()V",
+ (void *)CameraMetadata_dump },
// Parcelable interface
{ "nativeReadFromParcel",
"(Landroid/os/Parcel;)V",
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
new file mode 100644
index 0000000..7b686e7
--- /dev/null
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -0,0 +1,774 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "DngCreator_JNI"
+
+#include <system/camera_metadata.h>
+#include <camera/CameraMetadata.h>
+#include <img_utils/DngUtils.h>
+#include <img_utils/TagDefinitions.h>
+#include <img_utils/TiffIfd.h>
+#include <img_utils/TiffWriter.h>
+#include <img_utils/Output.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+#include <cutils/properties.h>
+
+#include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
+
+#include <jni.h>
+#include <JNIHelp.h>
+
+using namespace android;
+using namespace img_utils;
+
+#define BAIL_IF_INVALID(expr, jnienv, tagId) \
+ if ((expr) != OK) { \
+ jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
+ "Invalid metadata for tag %x", tagId); \
+ return; \
+ }
+
+#define BAIL_IF_EMPTY(entry, jnienv, tagId) \
+ if (entry.count == 0) { \
+ jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
+ "Missing metadata fields for tag %x", tagId); \
+ return; \
+ }
+
+#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
+
+static struct {
+ jfieldID mNativeContext;
+} gDngCreatorClassInfo;
+
+static struct {
+ jmethodID mWriteMethod;
+} gOutputStreamClassInfo;
+
+enum {
+ BITS_PER_SAMPLE = 16,
+ BYTES_PER_SAMPLE = 2,
+ TIFF_IFD_0 = 0
+};
+
+// ----------------------------------------------------------------------------
+
+// This class is not intended to be used across JNI calls.
+class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
+public:
+ JniOutputStream(JNIEnv* env, jobject outStream);
+
+ virtual ~JniOutputStream();
+
+ status_t open();
+ status_t write(const uint8_t* buf, size_t offset, size_t count);
+ status_t close();
+private:
+ enum {
+ BYTE_ARRAY_LENGTH = 1024
+ };
+ jobject mOutputStream;
+ JNIEnv* mEnv;
+ jbyteArray mByteArray;
+};
+
+JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
+ mEnv(env) {
+ mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
+ if (mByteArray == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
+ }
+}
+
+JniOutputStream::~JniOutputStream() {
+ mEnv->DeleteLocalRef(mByteArray);
+}
+
+status_t JniOutputStream::open() {
+ // Do nothing
+ return OK;
+}
+
+status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
+ while(count > 0) {
+ size_t len = BYTE_ARRAY_LENGTH;
+ len = (count > len) ? len : count;
+ mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
+
+ if (mEnv->ExceptionCheck()) {
+ return BAD_VALUE;
+ }
+
+ mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
+ 0, len);
+
+ if (mEnv->ExceptionCheck()) {
+ return BAD_VALUE;
+ }
+
+ count -= len;
+ offset += len;
+ }
+ return OK;
+}
+
+status_t JniOutputStream::close() {
+ // Do nothing
+ return OK;
+}
+
+// ----------------------------------------------------------------------------
+
+extern "C" {
+
+static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
+ ALOGV("%s:", __FUNCTION__);
+ return reinterpret_cast<TiffWriter*>(env->GetLongField(thiz,
+ gDngCreatorClassInfo.mNativeContext));
+}
+
+static void DngCreator_setCreator(JNIEnv* env, jobject thiz, sp<TiffWriter> writer) {
+ ALOGV("%s:", __FUNCTION__);
+ TiffWriter* current = DngCreator_getCreator(env, thiz);
+ if (writer != NULL) {
+ writer->incStrong((void*) DngCreator_setCreator);
+ }
+ if (current) {
+ current->decStrong((void*) DngCreator_setCreator);
+ }
+ env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
+ reinterpret_cast<jlong>(writer.get()));
+}
+
+static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
+ ALOGV("%s:", __FUNCTION__);
+
+ gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
+ ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
+ LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
+ "can't find android/hardware/camera2/DngCreator.%s",
+ ANDROID_DNGCREATOR_CTX_JNI_ID);
+
+ jclass outputStreamClazz = env->FindClass("java/io/OutputStream");
+ LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
+ gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
+ LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
+}
+
+static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
+ jobject resultsPtr) {
+ ALOGV("%s:", __FUNCTION__);
+ CameraMetadata characteristics;
+ CameraMetadata results;
+ if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
+ jniThrowException(env, "java/lang/AssertionError",
+ "No native metadata defined for camera characteristics.");
+ return;
+ }
+ if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
+ jniThrowException(env, "java/lang/AssertionError",
+ "No native metadata defined for capture results.");
+ return;
+ }
+
+ sp<TiffWriter> writer = new TiffWriter();
+
+ writer->addIfd(TIFF_IFD_0);
+
+ status_t err = OK;
+
+ const uint32_t samplesPerPixel = 1;
+ const uint32_t bitsPerSample = BITS_PER_SAMPLE;
+ const uint32_t bitsPerByte = BITS_PER_SAMPLE / BYTES_PER_SAMPLE;
+ uint32_t imageWidth = 0;
+ uint32_t imageHeight = 0;
+
+ OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
+
+ // TODO: Greensplit.
+ // TODO: UniqueCameraModel
+ // TODO: Add remaining non-essential tags
+ {
+ // Set orientation
+ uint16_t orientation = 1; // Normal
+ BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
+ TAG_ORIENTATION);
+ }
+
+ {
+ // Set subfiletype
+ uint32_t subfileType = 0; // Main image
+ BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
+ TAG_NEWSUBFILETYPE);
+ }
+
+ {
+ // Set bits per sample
+ uint16_t bits = static_cast<uint16_t>(bitsPerSample);
+ BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
+ TAG_BITSPERSAMPLE);
+ }
+
+ {
+ // Set compression
+ uint16_t compression = 1; // None
+ BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
+ TAG_COMPRESSION);
+ }
+
+ {
+ // Set dimensions
+ camera_metadata_entry entry =
+ characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH);
+ uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
+ uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
+ BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
+ TAG_IMAGEWIDTH);
+ BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
+ TAG_IMAGELENGTH);
+ imageWidth = width;
+ imageHeight = height;
+ }
+
+ {
+ // Set photometric interpretation
+ uint16_t interpretation = 32803;
+ BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
+ TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION);
+ }
+
+ {
+ // Set blacklevel tags
+ camera_metadata_entry entry =
+ characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
+ BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL);
+ const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
+ BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
+ TAG_BLACKLEVEL);
+
+ uint16_t repeatDim[2] = {2, 2};
+ BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
+ TAG_BLACKLEVELREPEATDIM);
+ }
+
+ {
+ // Set samples per pixel
+ uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
+ BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
+ env, TAG_SAMPLESPERPIXEL);
+ }
+
+ {
+ // Set planar configuration
+ uint16_t config = 1; // Chunky
+ BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
+ env, TAG_PLANARCONFIGURATION);
+ }
+
+ {
+ // Set CFA pattern dimensions
+ uint16_t repeatDim[2] = {2, 2};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
+ env, TAG_CFAREPEATPATTERNDIM);
+ }
+
+ {
+ // Set CFA pattern
+ camera_metadata_entry entry =
+ characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
+ BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN);
+ camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
+ static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
+ entry.data.u8[0]);
+ switch(cfa) {
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
+ uint8_t cfa[4] = {0, 1, 1, 2};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+ env, TAG_CFAPATTERN);
+ opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
+ break;
+ }
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
+ uint8_t cfa[4] = {1, 0, 2, 1};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+ env, TAG_CFAPATTERN);
+ opcodeCfaLayout = OpcodeListBuilder::CFA_GRBG;
+ break;
+ }
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
+ uint8_t cfa[4] = {1, 2, 0, 1};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+ env, TAG_CFAPATTERN);
+ opcodeCfaLayout = OpcodeListBuilder::CFA_GBRG;
+ break;
+ }
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
+ uint8_t cfa[4] = {2, 1, 1, 0};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+ env, TAG_CFAPATTERN);
+ opcodeCfaLayout = OpcodeListBuilder::CFA_BGGR;
+ break;
+ }
+ default: {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Invalid metadata for tag %d", TAG_CFAPATTERN);
+ return;
+ }
+ }
+ }
+
+ {
+ // Set CFA plane color
+ uint8_t cfaPlaneColor[3] = {0, 1, 2};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
+ env, TAG_CFAPLANECOLOR);
+ }
+
+ {
+ // Set CFA layout
+ uint16_t cfaLayout = 1;
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
+ env, TAG_CFALAYOUT);
+ }
+
+ {
+ // Set DNG version information
+ uint8_t version[4] = {1, 4, 0, 0};
+ BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
+ env, TAG_DNGVERSION);
+
+ uint8_t backwardVersion[4] = {1, 1, 0, 0};
+ BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
+ env, TAG_DNGBACKWARDVERSION);
+ }
+
+ {
+ // Set whitelevel
+ camera_metadata_entry entry =
+ characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
+ BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL);
+ uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
+ BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
+ TAG_WHITELEVEL);
+ }
+
+ {
+ // Set default scale
+ uint32_t defaultScale[4] = {1, 1, 1, 1};
+ BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
+ env, TAG_DEFAULTSCALE);
+ }
+
+ bool singleIlluminant = false;
+ {
+ // Set calibration illuminants
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
+ BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1);
+ camera_metadata_entry entry2 =
+ characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
+ if (entry2.count == 0) {
+ singleIlluminant = true;
+ }
+ uint16_t ref1 = entry1.data.u8[0];
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
+ TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1);
+
+ if (!singleIlluminant) {
+ uint16_t ref2 = entry2.data.u8[0];
+ BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
+ TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2);
+ }
+ }
+
+ {
+ // Set color transforms
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
+ BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1);
+
+ int32_t colorTransform1[entry1.count * 2];
+
+ size_t ctr = 0;
+ for(size_t i = 0; i < entry1.count; ++i) {
+ colorTransform1[ctr++] = entry1.data.r[i].numerator;
+ colorTransform1[ctr++] = entry1.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1, TIFF_IFD_0),
+ env, TAG_COLORMATRIX1);
+
+ if (!singleIlluminant) {
+ camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
+ BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2);
+ int32_t colorTransform2[entry2.count * 2];
+
+ ctr = 0;
+ for(size_t i = 0; i < entry2.count; ++i) {
+ colorTransform2[ctr++] = entry2.data.r[i].numerator;
+ colorTransform2[ctr++] = entry2.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2, TIFF_IFD_0),
+ env, TAG_COLORMATRIX2);
+ }
+ }
+
+ {
+ // Set calibration transforms
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
+ BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1);
+
+ int32_t calibrationTransform1[entry1.count * 2];
+
+ size_t ctr = 0;
+ for(size_t i = 0; i < entry1.count; ++i) {
+ calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
+ calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, calibrationTransform1,
+ TIFF_IFD_0), env, TAG_CAMERACALIBRATION1);
+
+ if (!singleIlluminant) {
+ camera_metadata_entry entry2 =
+ characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
+ BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2);
+ int32_t calibrationTransform2[entry2.count * 2];
+
+ ctr = 0;
+ for(size_t i = 0; i < entry2.count; ++i) {
+ calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
+ calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, calibrationTransform1,
+ TIFF_IFD_0), env, TAG_CAMERACALIBRATION2);
+ }
+ }
+
+ {
+ // Set forward transforms
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
+ BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1);
+
+ int32_t forwardTransform1[entry1.count * 2];
+
+ size_t ctr = 0;
+ for(size_t i = 0; i < entry1.count; ++i) {
+ forwardTransform1[ctr++] = entry1.data.r[i].numerator;
+ forwardTransform1[ctr++] = entry1.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
+ TIFF_IFD_0), env, TAG_FORWARDMATRIX1);
+
+ if (!singleIlluminant) {
+ camera_metadata_entry entry2 =
+ characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
+ BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2);
+ int32_t forwardTransform2[entry2.count * 2];
+
+ ctr = 0;
+ for(size_t i = 0; i < entry2.count; ++i) {
+ forwardTransform2[ctr++] = entry2.data.r[i].numerator;
+ forwardTransform2[ctr++] = entry2.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
+ TIFF_IFD_0), env, TAG_FORWARDMATRIX2);
+ }
+ }
+
+ {
+ // Set camera neutral
+ camera_metadata_entry entry =
+ results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
+ BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL);
+ uint32_t cameraNeutral[entry.count * 2];
+
+ size_t ctr = 0;
+ for(size_t i = 0; i < entry.count; ++i) {
+ cameraNeutral[ctr++] =
+ static_cast<uint32_t>(entry.data.r[i].numerator);
+ cameraNeutral[ctr++] =
+ static_cast<uint32_t>(entry.data.r[i].denominator);
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
+ TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL);
+ }
+
+ {
+ // Setup data strips
+ // TODO: Switch to tiled implementation.
+ uint32_t offset = 0;
+ BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &offset, TIFF_IFD_0), env,
+ TAG_STRIPOFFSETS);
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_ROWSPERSTRIP, 1, &imageHeight, TIFF_IFD_0), env,
+ TAG_ROWSPERSTRIP);
+
+ uint32_t byteCount = imageWidth * imageHeight * bitsPerSample * samplesPerPixel /
+ bitsPerByte;
+ BAIL_IF_INVALID(writer->addEntry(TAG_STRIPBYTECOUNTS, 1, &byteCount, TIFF_IFD_0), env,
+ TAG_STRIPBYTECOUNTS);
+ }
+
+ {
+ // Setup default crop + crop origin tags
+ uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
+ uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
+ if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
+ uint32_t defaultCropOrigin[] = {margin, margin};
+ uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
+ BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
+ TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN);
+ BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
+ TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE);
+ }
+ }
+
+ {
+ // Setup unique camera model tag
+ char model[PROPERTY_VALUE_MAX];
+ property_get("ro.product.model", model, "");
+
+ char manufacturer[PROPERTY_VALUE_MAX];
+ property_get("ro.product.manufacturer", manufacturer, "");
+
+ char brand[PROPERTY_VALUE_MAX];
+ property_get("ro.product.brand", brand, "");
+
+ String8 cameraModel(model);
+ cameraModel += "-";
+ cameraModel += manufacturer;
+ cameraModel += "-";
+ cameraModel += brand;
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
+ reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
+ TAG_UNIQUECAMERAMODEL);
+ }
+
+ {
+ // Setup opcode List 2
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
+ BAIL_IF_EMPTY(entry1, env, TAG_OPCODELIST2);
+ uint32_t lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
+ uint32_t lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
+
+ camera_metadata_entry entry2 =
+ results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
+ BAIL_IF_EMPTY(entry2, env, TAG_OPCODELIST2);
+ if (entry2.count == lsmWidth * lsmHeight * 4) {
+
+ OpcodeListBuilder builder;
+ status_t err = builder.addGainMapsForMetadata(lsmWidth,
+ lsmHeight,
+ 0,
+ 0,
+ imageHeight,
+ imageWidth,
+ opcodeCfaLayout,
+ entry2.data.f);
+ if (err == OK) {
+ size_t listSize = builder.getSize();
+ uint8_t opcodeListBuf[listSize];
+ err = builder.buildOpList(opcodeListBuf);
+ if (err == OK) {
+ BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
+ TIFF_IFD_0), env, TAG_OPCODELIST2);
+ } else {
+ ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
+ jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
+ }
+ } else {
+ ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
+ jniThrowRuntimeException(env, "failed to add lens shading map.");
+ }
+ } else {
+ ALOGW("%s: Lens shading map not present in results, skipping...", __FUNCTION__);
+ }
+ }
+
+ DngCreator_setCreator(env, thiz, writer);
+}
+
+static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
+ ALOGV("%s:", __FUNCTION__);
+ DngCreator_setCreator(env, thiz, NULL);
+}
+
+static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeSetOrientation is not implemented");
+}
+
+static void DngCreator_nativeSetThumbnailBitmap(JNIEnv* env, jobject thiz, jobject bitmap) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeSetThumbnailBitmap is not implemented");
+}
+
+static void DngCreator_nativeSetThumbnailImage(JNIEnv* env, jobject thiz, jint width, jint height,
+ jobject yBuffer, jint yRowStride, jint yPixStride, jobject uBuffer, jint uRowStride,
+ jint uPixStride, jobject vBuffer, jint vRowStride, jint vPixStride) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeSetThumbnailImage is not implemented");
+}
+
+static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
+ jint height, jobject inBuffer, jint rowStride, jint pixStride) {
+ ALOGV("%s:", __FUNCTION__);
+
+ sp<JniOutputStream> out = new JniOutputStream(env, outStream);
+ if(env->ExceptionCheck()) {
+ ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
+ return;
+ }
+
+ uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
+ if (pixelBytes == NULL) {
+ ALOGE("%s: Could not get native byte buffer", __FUNCTION__);
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid bytebuffer");
+ return;
+ }
+
+ TiffWriter* writer = DngCreator_getCreator(env, thiz);
+ if (writer == NULL) {
+ ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
+ jniThrowException(env, "java/lang/AssertionError",
+ "Write called with uninitialized DngCreator");
+ return;
+ }
+ // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
+ uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, TIFF_IFD_0)->getData<uint32_t>());
+ uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, TIFF_IFD_0)->getData<uint32_t>());
+ if (metadataWidth != width) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
+ "Metadata width %d doesn't match image width %d", metadataWidth, width);
+ return;
+ }
+
+ if (metadataHeight != height) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
+ "Metadata height %d doesn't match image height %d", metadataHeight, height);
+ return;
+ }
+
+ uint32_t stripOffset = writer->getTotalSize();
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &stripOffset, TIFF_IFD_0), env,
+ TAG_STRIPOFFSETS);
+
+ if (writer->write(out.get()) != OK) {
+ if (!env->ExceptionCheck()) {
+ jniThrowException(env, "java/io/IOException", "Failed to write metadata");
+ }
+ return;
+ }
+
+ size_t fullSize = rowStride * height;
+ jlong capacity = env->GetDirectBufferCapacity(inBuffer);
+ if (capacity < 0 || fullSize > capacity) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Invalid size %d for Image, size given in metadata is %d at current stride",
+ capacity, fullSize);
+ return;
+ }
+
+ if (pixStride == BYTES_PER_SAMPLE && rowStride == width * BYTES_PER_SAMPLE) {
+ if (out->write(pixelBytes, 0, fullSize) != OK || env->ExceptionCheck()) {
+ if (!env->ExceptionCheck()) {
+ jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
+ }
+ return;
+ }
+ } else if (pixStride == BYTES_PER_SAMPLE) {
+ for (size_t i = 0; i < height; ++i) {
+ if (out->write(pixelBytes, i * rowStride, pixStride * width) != OK ||
+ env->ExceptionCheck()) {
+ if (!env->ExceptionCheck()) {
+ jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
+ }
+ return;
+ }
+ }
+ } else {
+ for (size_t i = 0; i < height; ++i) {
+ for (size_t j = 0; j < width; ++j) {
+ if (out->write(pixelBytes, i * rowStride + j * pixStride,
+ BYTES_PER_SAMPLE) != OK || !env->ExceptionCheck()) {
+ if (env->ExceptionCheck()) {
+ jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
+ }
+ return;
+ }
+ }
+ }
+ }
+
+}
+
+static void DngCreator_nativeWriteByteBuffer(JNIEnv* env, jobject thiz, jobject outStream,
+ jobject rawBuffer, jlong offset) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeWriteByteBuffer is not implemented.");
+}
+
+static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
+ jobject inStream, jlong offset) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeWriteInputStream is not implemented.");
+}
+
+} /*extern "C" */
+
+static JNINativeMethod gDngCreatorMethods[] = {
+ {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
+ {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
+ "Landroid/hardware/camera2/impl/CameraMetadataNative;)V", (void*) DngCreator_init},
+ {"nativeDestroy", "()V", (void*) DngCreator_destroy},
+ {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
+ {"nativeSetThumbnailBitmap","(Landroid/graphics/Bitmap;)V",
+ (void*) DngCreator_nativeSetThumbnailBitmap},
+ {"nativeSetThumbnailImage",
+ "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V",
+ (void*) DngCreator_nativeSetThumbnailImage},
+ {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;II)V",
+ (void*) DngCreator_nativeWriteImage},
+ {"nativeWriteByteBuffer", "(Ljava/io/OutputStream;Ljava/nio/ByteBuffer;J)V",
+ (void*) DngCreator_nativeWriteByteBuffer},
+ {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;J)V",
+ (void*) DngCreator_nativeWriteInputStream},
+};
+
+int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/hardware/camera2/DngCreator", gDngCreatorMethods,
+ NELEM(gDngCreatorMethods));
+}
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 27d3f39..c5dd06f 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -45,7 +45,6 @@
#include <DisplayListRenderer.h>
#include <LayerRenderer.h>
#include <OpenGLRenderer.h>
-#include <SkiaShader.h>
#include <Stencil.h>
#include <Rect.h>
#include <RenderNode.h>
@@ -85,8 +84,6 @@
#define RENDERER_LOGD(...)
#endif
-#define MODIFIER_SHADER 2
-
// ----------------------------------------------------------------------------
static struct {
@@ -616,24 +613,6 @@
}
// ----------------------------------------------------------------------------
-// Shaders and color filters
-// ----------------------------------------------------------------------------
-
-static void android_view_GLES20Canvas_resetModifiers(JNIEnv* env, jobject clazz,
- jlong rendererPtr, jint modifiers) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- if (modifiers & MODIFIER_SHADER) renderer->resetShader();
-}
-
-static void android_view_GLES20Canvas_setupShader(JNIEnv* env, jobject clazz,
- jlong rendererPtr, jlong shaderPtr) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- SkiaShader* shader = reinterpret_cast<SkiaShader*>(shaderPtr);
- renderer->setupShader(shader);
-}
-
-
-// ----------------------------------------------------------------------------
// Draw filters
// ----------------------------------------------------------------------------
@@ -1091,9 +1070,6 @@
{ "nDrawPath", "(JJJ)V", (void*) android_view_GLES20Canvas_drawPath },
{ "nDrawLines", "(J[FIIJ)V", (void*) android_view_GLES20Canvas_drawLines },
- { "nResetModifiers", "(JI)V", (void*) android_view_GLES20Canvas_resetModifiers },
- { "nSetupShader", "(JJ)V", (void*) android_view_GLES20Canvas_setupShader },
-
{ "nSetupPaintFilter", "(JII)V", (void*) android_view_GLES20Canvas_setupPaintFilter },
{ "nResetPaintFilter", "(J)V", (void*) android_view_GLES20Canvas_resetPaintFilter },
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 6ae02e0..a590dbf 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -211,8 +211,9 @@
outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
- uint64_t bits = env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits);
- if (bits) {
+ BitSet64 bits =
+ BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits));
+ if (!bits.isEmpty()) {
jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj,
gPointerCoordsClassInfo.mPackedAxisValues));
if (valuesArray) {
@@ -221,11 +222,9 @@
uint32_t index = 0;
do {
- uint32_t axis = __builtin_ctzll(bits);
- uint64_t axisBit = 1LL << axis;
- bits &= ~axisBit;
+ uint32_t axis = bits.clearFirstMarkedBit();
outRawPointerCoords->setAxisValue(axis, values[index++]);
- } while (bits);
+ } while (!bits.isEmpty());
env->ReleasePrimitiveArrayCritical(valuesArray, values, JNI_ABORT);
env->DeleteLocalRef(valuesArray);
@@ -275,21 +274,19 @@
env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation,
rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
- const uint64_t unpackedAxisBits = 0
- | (1LL << AMOTION_EVENT_AXIS_X)
- | (1LL << AMOTION_EVENT_AXIS_Y)
- | (1LL << AMOTION_EVENT_AXIS_PRESSURE)
- | (1LL << AMOTION_EVENT_AXIS_SIZE)
- | (1LL << AMOTION_EVENT_AXIS_TOUCH_MAJOR)
- | (1LL << AMOTION_EVENT_AXIS_TOUCH_MINOR)
- | (1LL << AMOTION_EVENT_AXIS_TOOL_MAJOR)
- | (1LL << AMOTION_EVENT_AXIS_TOOL_MINOR)
- | (1LL << AMOTION_EVENT_AXIS_ORIENTATION);
-
uint64_t outBits = 0;
- uint64_t remainingBits = rawPointerCoords->bits & ~unpackedAxisBits;
- if (remainingBits) {
- uint32_t packedAxesCount = __builtin_popcountll(remainingBits);
+ BitSet64 bits = BitSet64(rawPointerCoords->bits);
+ bits.clearBit(AMOTION_EVENT_AXIS_X);
+ bits.clearBit(AMOTION_EVENT_AXIS_Y);
+ bits.clearBit(AMOTION_EVENT_AXIS_PRESSURE);
+ bits.clearBit(AMOTION_EVENT_AXIS_SIZE);
+ bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+ bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MINOR);
+ bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MAJOR);
+ bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MINOR);
+ bits.clearBit(AMOTION_EVENT_AXIS_ORIENTATION);
+ if (!bits.isEmpty()) {
+ uint32_t packedAxesCount = bits.count();
jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount,
outPointerCoordsObj);
if (!outValuesArray) {
@@ -302,12 +299,10 @@
const float* values = rawPointerCoords->values;
uint32_t index = 0;
do {
- uint32_t axis = __builtin_ctzll(remainingBits);
- uint64_t axisBit = 1LL << axis;
- remainingBits &= ~axisBit;
- outBits |= axisBit;
+ uint32_t axis = bits.clearFirstMarkedBit();
+ outBits |= BitSet64::valueForBit(axis);
outValues[index++] = rawPointerCoords->getAxisValue(axis);
- } while (remainingBits);
+ } while (!bits.isEmpty());
env->ReleasePrimitiveArrayCritical(outValuesArray, outValues, 0);
env->DeleteLocalRef(outValuesArray);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 48fb729..5bc0f62 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -299,6 +299,12 @@
proxy->fence();
}
+static void android_view_ThreadedRenderer_notifyFramePending(JNIEnv* env, jobject clazz,
+ jlong proxyPtr) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ proxy->notifyFramePending();
+}
+
#endif
// ----------------------------------------------------------------------------
@@ -329,6 +335,7 @@
{ "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto },
{ "nDestroyLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_destroyLayer },
{ "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence },
+ { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
#endif
};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1874fd8..14141d7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1014,6 +1014,13 @@
android:description="@string/permdesc_sim_communication"
android:protectionLevel="dangerous" />
+ <!-- Allows TvInputService to access underlying TV input hardware such as
+ built-in tuners and HDMI-in's.
+ @hide This should only be used by OEM's TvInputService's.
+ -->
+ <permission android:name="android.permission.TV_INPUT_HARDWARE"
+ android:protectionLevel="signatureOrSystem" />
+
<!-- =========================================== -->
<!-- Permissions associated with audio capture -->
<!-- =========================================== -->
diff --git a/core/res/res/anim/slide_in_micro.xml b/core/res/res/anim/slide_in_micro.xml
new file mode 100644
index 0000000..6320e80
--- /dev/null
+++ b/core/res/res/anim/slide_in_micro.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/slide_in_micro.xml
+**
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate android:fromXDelta="100%p" android:toXDelta="0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
+</set>
diff --git a/core/res/res/anim/slide_out_micro.xml b/core/res/res/anim/slide_out_micro.xml
new file mode 100644
index 0000000..4cb6df0
--- /dev/null
+++ b/core/res/res/anim/slide_out_micro.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/slide_out_micro.xml
+**
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate android:fromXDelta="0" android:toXDelta="100%p"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
+</set>
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png
deleted file mode 100644
index 1880a15..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png
deleted file mode 100644
index aecb4d2..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png
deleted file mode 100644
index 8401f91..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png
deleted file mode 100644
index 5832865..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png
deleted file mode 100644
index 6d14962..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png
deleted file mode 100644
index aee057c..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png
deleted file mode 100644
index fb5801e..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png
deleted file mode 100644
index fdb5271..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png
deleted file mode 100644
index b8c7397..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png
deleted file mode 100644
index d0395a8..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png
deleted file mode 100644
index 59bb437..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png
deleted file mode 100644
index c053b90..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png
deleted file mode 100644
index eb30a79..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png
deleted file mode 100644
index 1af0bff..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png
deleted file mode 100644
index 3b36e7d..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png
deleted file mode 100644
index c12d20a..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_off_qntm_alpha.png
deleted file mode 100644
index 5bc1d90..0000000
--- a/core/res/res/drawable-hdpi/btn_check_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_on_qntm_alpha.png
deleted file mode 100644
index e5de2c1..0000000
--- a/core/res/res/drawable-hdpi/btn_check_on_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_000.png
new file mode 100644
index 0000000..3cb4073
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_001.png
new file mode 100644
index 0000000..8fd1480
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_002.png
new file mode 100644
index 0000000..d35b579
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_003.png
new file mode 100644
index 0000000..543c6bc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_004.png
new file mode 100644
index 0000000..4fc3c40
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_005.png
new file mode 100644
index 0000000..c184535
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_006.png
new file mode 100644
index 0000000..9f9dd43
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_007.png
new file mode 100644
index 0000000..8c629ce
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_008.png
new file mode 100644
index 0000000..81134b5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_009.png
new file mode 100644
index 0000000..baa5860
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_010.png
new file mode 100644
index 0000000..d7e28366
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_011.png
new file mode 100644
index 0000000..6f24795
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_012.png
new file mode 100644
index 0000000..22f997d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_013.png
new file mode 100644
index 0000000..85f4471
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_014.png
new file mode 100644
index 0000000..ad483c9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_015.png
new file mode 100644
index 0000000..f24c2fb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_000.png
new file mode 100644
index 0000000..7a9e9bd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_001.png
new file mode 100644
index 0000000..af04902
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_002.png
new file mode 100644
index 0000000..32a6e94
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_003.png
new file mode 100644
index 0000000..c1b4b37
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_004.png
new file mode 100644
index 0000000..34d3ade
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_005.png
new file mode 100644
index 0000000..3d5db53
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_006.png
new file mode 100644
index 0000000..ea35437
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_007.png
new file mode 100644
index 0000000..48744f8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_008.png
new file mode 100644
index 0000000..f654517
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_009.png
new file mode 100644
index 0000000..16f959a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_010.png
new file mode 100644
index 0000000..98c754b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_011.png
new file mode 100644
index 0000000..5827dc2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_012.png
new file mode 100644
index 0000000..9850d74
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_013.png
new file mode 100644
index 0000000..03ab06b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_014.png
new file mode 100644
index 0000000..11cdd88
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_015.png
new file mode 100644
index 0000000..874edbf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png
deleted file mode 100644
index 882365b..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png
deleted file mode 100644
index f6c7094..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png
deleted file mode 100644
index 0e326c9..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png
deleted file mode 100644
index 8bf1170..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png
deleted file mode 100644
index cedb66e..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png
deleted file mode 100644
index 257d7ba..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png
deleted file mode 100644
index e07b36e..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png
deleted file mode 100644
index ef94200..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png
deleted file mode 100644
index ad67004..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png
deleted file mode 100644
index 50796e2..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png
deleted file mode 100644
index ba7be9e..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png
deleted file mode 100644
index bdbfe78..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png
deleted file mode 100644
index fe89951..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png
deleted file mode 100644
index 840c88f..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png
deleted file mode 100644
index 621d1d2..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png
deleted file mode 100644
index fd8be89..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed_qntm_alpha.png
deleted file mode 100644
index 7bef530..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_off_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_off_qntm_alpha.png
deleted file mode 100644
index ae50dd5..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed_qntm_alpha.png
deleted file mode 100644
index 0678dbb..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_on_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_on_qntm_alpha.png
deleted file mode 100644
index f332925..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_on_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_000.png
new file mode 100644
index 0000000..da88e98
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_001.png
new file mode 100644
index 0000000..907d92d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_002.png
new file mode 100644
index 0000000..9d24dc1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_003.png
new file mode 100644
index 0000000..8aa2605
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_004.png
new file mode 100644
index 0000000..b4cdf02
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_005.png
new file mode 100644
index 0000000..0724ed7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_006.png
new file mode 100644
index 0000000..c9bd4e3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_007.png
new file mode 100644
index 0000000..5630ec3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_008.png
new file mode 100644
index 0000000..4bf666c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_009.png
new file mode 100644
index 0000000..dffaa07
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_010.png
new file mode 100644
index 0000000..5f86e18
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_011.png
new file mode 100644
index 0000000..9b50aef
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_012.png
new file mode 100644
index 0000000..1cf5e7f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_013.png
new file mode 100644
index 0000000..2bb641a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_014.png
new file mode 100644
index 0000000..08e7485
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_015.png
new file mode 100644
index 0000000..519b5a3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_000.png
new file mode 100644
index 0000000..0d3e1e7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_001.png
new file mode 100644
index 0000000..88c4a9e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_002.png
new file mode 100644
index 0000000..8fa2e88
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_003.png
new file mode 100644
index 0000000..53dd9d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_004.png
new file mode 100644
index 0000000..e235195
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_005.png
new file mode 100644
index 0000000..1721284
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_006.png
new file mode 100644
index 0000000..31819fa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_007.png
new file mode 100644
index 0000000..5de44b9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_008.png
new file mode 100644
index 0000000..aa20f65
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_009.png
new file mode 100644
index 0000000..c379ba7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_010.png
new file mode 100644
index 0000000..e23b410
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_011.png
new file mode 100644
index 0000000..a9543dc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_012.png
new file mode 100644
index 0000000..2473b78
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_013.png
new file mode 100644
index 0000000..b4acc9c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_014.png
new file mode 100644
index 0000000..c9cf344
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_015.png
new file mode 100644
index 0000000..a8c390e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_000.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_001.png
new file mode 100644
index 0000000..e062f61
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_002.png
new file mode 100644
index 0000000..7737646
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_003.png
new file mode 100644
index 0000000..65ff45e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_004.png
new file mode 100644
index 0000000..11aaec0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_005.png
new file mode 100644
index 0000000..9e1b60f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_006.png
new file mode 100644
index 0000000..1e45687
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_007.png
new file mode 100644
index 0000000..1e45687
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_008.png
new file mode 100644
index 0000000..6c48456
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_009.png
new file mode 100644
index 0000000..a4d084b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_010.png
new file mode 100644
index 0000000..1e1a1b0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_011.png
new file mode 100644
index 0000000..1e1a1b0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_012.png
new file mode 100644
index 0000000..1e1a1b0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_013.png
new file mode 100644
index 0000000..1e1a1b0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_014.png
new file mode 100644
index 0000000..1e1a1b0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_000.png
new file mode 100644
index 0000000..cf09f97
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_001.png
new file mode 100644
index 0000000..3218e66
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_002.png
new file mode 100644
index 0000000..0acff03
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_003.png
new file mode 100644
index 0000000..c93adf4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_004.png
new file mode 100644
index 0000000..5d8ddc96
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_005.png
new file mode 100644
index 0000000..47206a4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_006.png
new file mode 100644
index 0000000..7d6a91f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_007.png
new file mode 100644
index 0000000..e062f61
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_008.png
new file mode 100644
index 0000000..b0f0dde
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_009.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_010.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_011.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_012.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_013.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_014.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-hdpi/btn_toggle_indicator_qntm_alpha.9.png
new file mode 100644
index 0000000..68e17ad
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_indicator_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-hdpi/btn_toggle_qntm_alpha.9.png
new file mode 100644
index 0000000..879d9c2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_000.png
new file mode 100644
index 0000000..281923e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_001.png
new file mode 100644
index 0000000..e91d4fb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_002.png
new file mode 100644
index 0000000..15baded
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_003.png
new file mode 100644
index 0000000..3d5899f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_004.png
new file mode 100644
index 0000000..e2277bd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_005.png
new file mode 100644
index 0000000..b502e22
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_000.png
new file mode 100644
index 0000000..a70be2c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_001.png
new file mode 100644
index 0000000..9442316
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_002.png
new file mode 100644
index 0000000..33db4a88
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_003.png
new file mode 100644
index 0000000..4e1cd16
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_004.png
new file mode 100644
index 0000000..d5254f4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_005.png
new file mode 100644
index 0000000..7aa0a3e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
deleted file mode 100644
index 73e8f1c..0000000
--- a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
deleted file mode 100644
index ff6affe..0000000
--- a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_track_qntm_alpha.9.png
index b11de9e..ac1fc23 100644
--- a/core/res/res/drawable-hdpi/switch_track_qntm_alpha.9.png
+++ b/core/res/res/drawable-hdpi/switch_track_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle_left_qntm_alpha.png b/core/res/res/drawable-hdpi/text_select_handle_left_qntm_alpha.png
index 598b98c..9cdc25b 100644
--- a/core/res/res/drawable-hdpi/text_select_handle_left_qntm_alpha.png
+++ b/core/res/res/drawable-hdpi/text_select_handle_left_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle_right_qntm_alpha.png b/core/res/res/drawable-hdpi/text_select_handle_right_qntm_alpha.png
index 79fe7c5..276d480 100644
--- a/core/res/res/drawable-hdpi/text_select_handle_right_qntm_alpha.png
+++ b/core/res/res/drawable-hdpi/text_select_handle_right_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png
deleted file mode 100644
index 0f44ff9..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png
deleted file mode 100644
index 9d5dda0..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png
deleted file mode 100644
index e4ce802..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png
deleted file mode 100644
index d1806ac..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png
deleted file mode 100644
index ab9315b..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png
deleted file mode 100644
index 46e90e6..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png
deleted file mode 100644
index e8c56ff..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png
deleted file mode 100644
index 59dcb7e..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png
deleted file mode 100644
index e9bd4a2..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png
deleted file mode 100644
index 1d05037..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png
deleted file mode 100644
index 91b40de..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png
deleted file mode 100644
index c531cab..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png
deleted file mode 100644
index 11bb387..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png
deleted file mode 100644
index 8843210..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png
deleted file mode 100644
index 6ff2f3d..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png
deleted file mode 100644
index a03c1e2..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_off_qntm_alpha.png
deleted file mode 100644
index 2ab6b7f..0000000
--- a/core/res/res/drawable-mdpi/btn_check_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_on_qntm_alpha.png
deleted file mode 100644
index 2211d83..0000000
--- a/core/res/res/drawable-mdpi/btn_check_on_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_000.png
new file mode 100644
index 0000000..9759818
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_001.png
new file mode 100644
index 0000000..4eb2c4f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_002.png
new file mode 100644
index 0000000..e6d6b42
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_003.png
new file mode 100644
index 0000000..03cb23a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_004.png
new file mode 100644
index 0000000..bfe3c3d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_005.png
new file mode 100644
index 0000000..65bdf42
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_006.png
new file mode 100644
index 0000000..44f9614b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_007.png
new file mode 100644
index 0000000..cf8ec38
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_008.png
new file mode 100644
index 0000000..4d624b3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_009.png
new file mode 100644
index 0000000..7c4eb7f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_010.png
new file mode 100644
index 0000000..e90dd31
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_011.png
new file mode 100644
index 0000000..831c0e8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_012.png
new file mode 100644
index 0000000..7355dfd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_013.png
new file mode 100644
index 0000000..be71a69
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_014.png
new file mode 100644
index 0000000..a4a185b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_015.png
new file mode 100644
index 0000000..8d0386f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_000.png
new file mode 100644
index 0000000..70793c4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_001.png
new file mode 100644
index 0000000..632082b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_002.png
new file mode 100644
index 0000000..e7fc5fb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_003.png
new file mode 100644
index 0000000..91a0a33
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_004.png
new file mode 100644
index 0000000..3bd90d6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_005.png
new file mode 100644
index 0000000..5ac39ec
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_006.png
new file mode 100644
index 0000000..4181983
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_007.png
new file mode 100644
index 0000000..c8b04df
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_008.png
new file mode 100644
index 0000000..b7b3a9f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_009.png
new file mode 100644
index 0000000..62bc4ed
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_010.png
new file mode 100644
index 0000000..ac463ad
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_011.png
new file mode 100644
index 0000000..12b605d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_012.png
new file mode 100644
index 0000000..63a3c6a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_013.png
new file mode 100644
index 0000000..17660c4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_014.png
new file mode 100644
index 0000000..7d9de3d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_015.png
new file mode 100644
index 0000000..8aa1be2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png
deleted file mode 100644
index 0a22e1a..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png
deleted file mode 100644
index 2e2469c..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png
deleted file mode 100644
index c1054d9..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png
deleted file mode 100644
index cf8d80a..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png
deleted file mode 100644
index 9d9e870..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png
deleted file mode 100644
index 1bad701..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png
deleted file mode 100644
index a84a54f..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png
deleted file mode 100644
index 4d8050b..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png
deleted file mode 100644
index 374172c..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png
deleted file mode 100644
index 233036e..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png
deleted file mode 100644
index 61d9b58..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png
deleted file mode 100644
index 274e983..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png
deleted file mode 100644
index acf16e5..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png
deleted file mode 100644
index ee48241..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png
deleted file mode 100644
index dbbb736..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png
deleted file mode 100644
index bcabd0d..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed_qntm_alpha.png
deleted file mode 100644
index 713fee8..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_off_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_off_qntm_alpha.png
deleted file mode 100644
index dcb90d0..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_000.png
new file mode 100644
index 0000000..a2b7fce
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_001.png
new file mode 100644
index 0000000..fe0d3b1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_002.png
new file mode 100644
index 0000000..d66d00d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_003.png
new file mode 100644
index 0000000..2f2f5cd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_004.png
new file mode 100644
index 0000000..72c9495
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_005.png
new file mode 100644
index 0000000..7d9090f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_006.png
new file mode 100644
index 0000000..c5442e8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_007.png
new file mode 100644
index 0000000..ca80cdb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_008.png
new file mode 100644
index 0000000..d41a10b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_009.png
new file mode 100644
index 0000000..262c838
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_010.png
new file mode 100644
index 0000000..7f6ea8ba
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_011.png
new file mode 100644
index 0000000..8d50a81
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_012.png
new file mode 100644
index 0000000..0725a68
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_013.png
new file mode 100644
index 0000000..6191a4b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_014.png
new file mode 100644
index 0000000..1904d74
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_015.png
new file mode 100644
index 0000000..bec8dda
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_000.png
new file mode 100644
index 0000000..54ef480
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_001.png
new file mode 100644
index 0000000..55c5163
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_002.png
new file mode 100644
index 0000000..0fe2a897
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_003.png
new file mode 100644
index 0000000..86efab7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_004.png
new file mode 100644
index 0000000..c0a5ca5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_005.png
new file mode 100644
index 0000000..ec55175
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_006.png
new file mode 100644
index 0000000..3e4a690
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_007.png
new file mode 100644
index 0000000..da49734
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_008.png
new file mode 100644
index 0000000..471cda1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_009.png
new file mode 100644
index 0000000..d560262
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_010.png
new file mode 100644
index 0000000..f6096b4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_011.png
new file mode 100644
index 0000000..9e2500b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_012.png
new file mode 100644
index 0000000..efbac99
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_013.png
new file mode 100644
index 0000000..676f0ca
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_014.png
new file mode 100644
index 0000000..4803157
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_015.png
new file mode 100644
index 0000000..4f8a162
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_000.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_001.png
new file mode 100644
index 0000000..3617168
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_002.png
new file mode 100644
index 0000000..e4366f4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_003.png
new file mode 100644
index 0000000..ea4533b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_004.png
new file mode 100644
index 0000000..94aedbb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_005.png
new file mode 100644
index 0000000..ef84578
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_006.png
new file mode 100644
index 0000000..4de2321
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_007.png
new file mode 100644
index 0000000..4de2321
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_008.png
new file mode 100644
index 0000000..d62fbd5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_009.png
new file mode 100644
index 0000000..3d87c4e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_010.png
new file mode 100644
index 0000000..536ed46
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_011.png
new file mode 100644
index 0000000..536ed46
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_012.png
new file mode 100644
index 0000000..536ed46
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_013.png
new file mode 100644
index 0000000..536ed46
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_014.png
new file mode 100644
index 0000000..536ed46
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_000.png
new file mode 100644
index 0000000..f5b660d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_001.png
new file mode 100644
index 0000000..9e4db6c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_002.png
new file mode 100644
index 0000000..7de2128
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_003.png
new file mode 100644
index 0000000..1980c2c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_004.png
new file mode 100644
index 0000000..6e73ef0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_005.png
new file mode 100644
index 0000000..f897392
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_006.png
new file mode 100644
index 0000000..74a6ebd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_007.png
new file mode 100644
index 0000000..3617168
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_008.png
new file mode 100644
index 0000000..884eb66
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_009.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_010.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_011.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_012.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_013.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_014.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-mdpi/btn_toggle_indicator_qntm_alpha.9.png
new file mode 100644
index 0000000..e5bface
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_indicator_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-mdpi/btn_toggle_qntm_alpha.9.png
new file mode 100644
index 0000000..dca86ea
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_000.png
new file mode 100644
index 0000000..377a6b4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_001.png
new file mode 100644
index 0000000..0859f62
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_002.png
new file mode 100644
index 0000000..bf5cdcd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_003.png
new file mode 100644
index 0000000..4cd177d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_004.png
new file mode 100644
index 0000000..dfe39ca
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_005.png
new file mode 100644
index 0000000..5d3ab99
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_000.png
new file mode 100644
index 0000000..80922eb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_001.png
new file mode 100644
index 0000000..aa77044
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_002.png
new file mode 100644
index 0000000..7b099db
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_003.png
new file mode 100644
index 0000000..088c86a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_004.png
new file mode 100644
index 0000000..3c6b9bc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_005.png
new file mode 100644
index 0000000..adf5100
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
deleted file mode 100644
index 8949b52..0000000
--- a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
deleted file mode 100644
index d727683..0000000
--- a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_track_qntm_alpha.9.png
index 8991421..b6538e4 100644
--- a/core/res/res/drawable-mdpi/switch_track_qntm_alpha.9.png
+++ b/core/res/res/drawable-mdpi/switch_track_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_left_qntm_alpha.png b/core/res/res/drawable-mdpi/text_select_handle_left_qntm_alpha.png
index 506a186..95c0168 100644
--- a/core/res/res/drawable-mdpi/text_select_handle_left_qntm_alpha.png
+++ b/core/res/res/drawable-mdpi/text_select_handle_left_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_right_qntm_alpha.png b/core/res/res/drawable-mdpi/text_select_handle_right_qntm_alpha.png
index fb0e926..569332a 100644
--- a/core/res/res/drawable-mdpi/text_select_handle_right_qntm_alpha.png
+++ b/core/res/res/drawable-mdpi/text_select_handle_right_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png
deleted file mode 100644
index 25500e8..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png
deleted file mode 100644
index b136e25..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png
deleted file mode 100644
index 6a94e30..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png
deleted file mode 100644
index d386421..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png
deleted file mode 100644
index c811385..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png
deleted file mode 100644
index 58b3267..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png
deleted file mode 100644
index 0659e72..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png
deleted file mode 100644
index b4227d1..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png
deleted file mode 100644
index 714ef00..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png
deleted file mode 100644
index 139595b..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png
deleted file mode 100644
index 4491107..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png
deleted file mode 100644
index 20eb752..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png
deleted file mode 100644
index 532c9f2..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png
deleted file mode 100644
index 0d78a32..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png
deleted file mode 100644
index af29678..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png
deleted file mode 100644
index 23eb9e3..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_off_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_off_qntm_alpha.png
deleted file mode 100644
index 5d820ae..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_on_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_on_qntm_alpha.png
deleted file mode 100644
index 019c92e..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_on_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_000.png
new file mode 100644
index 0000000..2347643
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_001.png
new file mode 100644
index 0000000..70aaa01
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_002.png
new file mode 100644
index 0000000..01e498a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_003.png
new file mode 100644
index 0000000..71d1cf7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_004.png
new file mode 100644
index 0000000..d1e7b1d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_005.png
new file mode 100644
index 0000000..7db7d06
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_006.png
new file mode 100644
index 0000000..dadb62e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_007.png
new file mode 100644
index 0000000..f87f744
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_008.png
new file mode 100644
index 0000000..be99d87
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_009.png
new file mode 100644
index 0000000..f83bc05
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_010.png
new file mode 100644
index 0000000..870071d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_011.png
new file mode 100644
index 0000000..3a18414
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_012.png
new file mode 100644
index 0000000..f3d1187
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_013.png
new file mode 100644
index 0000000..4078cca
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_014.png
new file mode 100644
index 0000000..d4849b5
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_015.png
new file mode 100644
index 0000000..6e2af72
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_000.png
new file mode 100644
index 0000000..9244174
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_001.png
new file mode 100644
index 0000000..8c7fe95
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_002.png
new file mode 100644
index 0000000..71eb1d0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_003.png
new file mode 100644
index 0000000..613f38a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_004.png
new file mode 100644
index 0000000..2d20ccc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_005.png
new file mode 100644
index 0000000..407f78d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_006.png
new file mode 100644
index 0000000..1bf24b0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_007.png
new file mode 100644
index 0000000..a450bd0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_008.png
new file mode 100644
index 0000000..63ba593
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_009.png
new file mode 100644
index 0000000..6d05e5a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_010.png
new file mode 100644
index 0000000..1c8cd8f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_011.png
new file mode 100644
index 0000000..b8bc564
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_012.png
new file mode 100644
index 0000000..3d80128
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_013.png
new file mode 100644
index 0000000..c21dfba
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_014.png
new file mode 100644
index 0000000..2dfe90d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_015.png
new file mode 100644
index 0000000..5f40d73
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png
deleted file mode 100644
index cd11e14..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png
deleted file mode 100644
index b10db83..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png
deleted file mode 100644
index efeb6fb..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png
deleted file mode 100644
index 83080af..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png
deleted file mode 100644
index b9cc322..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png
deleted file mode 100644
index 3b5f9c4..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png
deleted file mode 100644
index 58c93db..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png
deleted file mode 100644
index 0f1d010..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png
deleted file mode 100644
index 05a7a0f..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png
deleted file mode 100644
index 9345035..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png
deleted file mode 100644
index 5f149b7..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png
deleted file mode 100644
index 191f369..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png
deleted file mode 100644
index 44e08e6..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png
deleted file mode 100644
index 5a9dfa0..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png
deleted file mode 100644
index ee921c6..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png
deleted file mode 100644
index 567bb0c..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_off_pressed_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_off_pressed_qntm_alpha.png
deleted file mode 100644
index 2fd964e..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_off_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_off_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_off_qntm_alpha.png
deleted file mode 100644
index 8873cd6..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_000.png
new file mode 100644
index 0000000..b54c6ff
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_001.png
new file mode 100644
index 0000000..fff7056
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_002.png
new file mode 100644
index 0000000..026462d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_003.png
new file mode 100644
index 0000000..26cc8de
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_004.png
new file mode 100644
index 0000000..c055fff
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_005.png
new file mode 100644
index 0000000..a22e780
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_006.png
new file mode 100644
index 0000000..357374c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_007.png
new file mode 100644
index 0000000..71d4667
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_008.png
new file mode 100644
index 0000000..2ed175e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_009.png
new file mode 100644
index 0000000..e0f7d8e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_010.png
new file mode 100644
index 0000000..62b0578
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_011.png
new file mode 100644
index 0000000..4d6ef4a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_012.png
new file mode 100644
index 0000000..37cee2d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_013.png
new file mode 100644
index 0000000..a8bc25f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_014.png
new file mode 100644
index 0000000..cf68d93
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_015.png
new file mode 100644
index 0000000..96834bc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_000.png
new file mode 100644
index 0000000..d068dbe
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_001.png
new file mode 100644
index 0000000..4aabb1e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_002.png
new file mode 100644
index 0000000..bbac8e4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_003.png
new file mode 100644
index 0000000..2fc7459
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_004.png
new file mode 100644
index 0000000..83c6d0e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_005.png
new file mode 100644
index 0000000..45c08d7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_006.png
new file mode 100644
index 0000000..05b7dfb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_007.png
new file mode 100644
index 0000000..baf9964
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_008.png
new file mode 100644
index 0000000..d6e0369
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_009.png
new file mode 100644
index 0000000..3f35270
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_010.png
new file mode 100644
index 0000000..a5b34dc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_011.png
new file mode 100644
index 0000000..361967b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_012.png
new file mode 100644
index 0000000..c478bb7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_013.png
new file mode 100644
index 0000000..075fa0c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_014.png
new file mode 100644
index 0000000..d9e364b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_015.png
new file mode 100644
index 0000000..9924496
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_000.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_001.png
new file mode 100644
index 0000000..7bd99fe
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_002.png
new file mode 100644
index 0000000..2ef623b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_003.png
new file mode 100644
index 0000000..19db3e0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_004.png
new file mode 100644
index 0000000..984c3c5
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_005.png
new file mode 100644
index 0000000..6454190
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_006.png
new file mode 100644
index 0000000..cee9393
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_007.png
new file mode 100644
index 0000000..cee9393
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_008.png
new file mode 100644
index 0000000..437ffdd
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_009.png
new file mode 100644
index 0000000..d2e14ad
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_010.png
new file mode 100644
index 0000000..4e2f5bc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_011.png
new file mode 100644
index 0000000..4e2f5bc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_012.png
new file mode 100644
index 0000000..4e2f5bc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_013.png
new file mode 100644
index 0000000..4e2f5bc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_014.png
new file mode 100644
index 0000000..4e2f5bc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_000.png
new file mode 100644
index 0000000..f1bcfa3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_001.png
new file mode 100644
index 0000000..ede2fec
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_002.png
new file mode 100644
index 0000000..94ce017
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_003.png
new file mode 100644
index 0000000..647cfe3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_004.png
new file mode 100644
index 0000000..b3bf923
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_005.png
new file mode 100644
index 0000000..ae95b2b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_006.png
new file mode 100644
index 0000000..b8e4bd6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_007.png
new file mode 100644
index 0000000..ec6d6d7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_008.png
new file mode 100644
index 0000000..c0e493f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_009.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_010.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_011.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_012.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_013.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_014.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/btn_toggle_indicator_qntm_alpha.9.png
new file mode 100644
index 0000000..dff391f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_indicator_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/btn_toggle_qntm_alpha.9.png
new file mode 100644
index 0000000..b135338
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_000.png
new file mode 100644
index 0000000..ea09a31
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_001.png
new file mode 100644
index 0000000..f9a4391
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_002.png
new file mode 100644
index 0000000..d9606e1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_003.png
new file mode 100644
index 0000000..df2d9d0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_004.png
new file mode 100644
index 0000000..625a322
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_005.png
new file mode 100644
index 0000000..79e8dde
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_000.png
new file mode 100644
index 0000000..e99c266
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_001.png
new file mode 100644
index 0000000..f0329a4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_002.png
new file mode 100644
index 0000000..42c40b7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_003.png
new file mode 100644
index 0000000..807491f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_004.png
new file mode 100644
index 0000000..dfec9cc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_005.png
new file mode 100644
index 0000000..0ed59ea
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
deleted file mode 100644
index a7a972c..0000000
--- a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
deleted file mode 100644
index dd8910b..0000000
--- a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_track_qntm_alpha.9.png
index 4970f56..d6a0ab2 100644
--- a/core/res/res/drawable-xhdpi/switch_track_qntm_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/switch_track_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_select_handle_left_qntm_alpha.png b/core/res/res/drawable-xhdpi/text_select_handle_left_qntm_alpha.png
index 38b8e8b..a01ac10 100644
--- a/core/res/res/drawable-xhdpi/text_select_handle_left_qntm_alpha.png
+++ b/core/res/res/drawable-xhdpi/text_select_handle_left_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_select_handle_right_qntm_alpha.png b/core/res/res/drawable-xhdpi/text_select_handle_right_qntm_alpha.png
index d6002a7..d3602d9 100644
--- a/core/res/res/drawable-xhdpi/text_select_handle_right_qntm_alpha.png
+++ b/core/res/res/drawable-xhdpi/text_select_handle_right_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png
deleted file mode 100644
index 1881f54..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png
deleted file mode 100644
index 6f8ec2d..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png
deleted file mode 100644
index c954ed9..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png
deleted file mode 100644
index 9d1a47e..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png
deleted file mode 100644
index ce63631..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png
deleted file mode 100644
index 430c134..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png
deleted file mode 100644
index cdebf83..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png
deleted file mode 100644
index 40ceadb..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png
deleted file mode 100644
index fb13eb2..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png
deleted file mode 100644
index d716fba..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png
deleted file mode 100644
index b8be041..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png
deleted file mode 100644
index bad0c3c..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png
deleted file mode 100644
index a6368fb..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png
deleted file mode 100644
index 234e5d1..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png
deleted file mode 100644
index 3e7796d..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png
deleted file mode 100644
index 0673999..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_off_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_off_qntm_alpha.png
deleted file mode 100644
index 2a17861..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_on_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_on_qntm_alpha.png
deleted file mode 100644
index 61067ac..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_on_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_000.png
new file mode 100644
index 0000000..b754381
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_001.png
new file mode 100644
index 0000000..517d7a7
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_002.png
new file mode 100644
index 0000000..2c1d5b6
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_003.png
new file mode 100644
index 0000000..0c6ff7e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_004.png
new file mode 100644
index 0000000..0796601
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_005.png
new file mode 100644
index 0000000..9b4e0f8
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_006.png
new file mode 100644
index 0000000..25767eb
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_007.png
new file mode 100644
index 0000000..cd0951f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_008.png
new file mode 100644
index 0000000..9ae8165
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_009.png
new file mode 100644
index 0000000..efd9bc6
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_010.png
new file mode 100644
index 0000000..fccbc9d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_011.png
new file mode 100644
index 0000000..dddafca
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_012.png
new file mode 100644
index 0000000..7e37433
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_013.png
new file mode 100644
index 0000000..9bc22de
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_014.png
new file mode 100644
index 0000000..507ed10
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_015.png
new file mode 100644
index 0000000..6a21c7f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_000.png
new file mode 100644
index 0000000..0d544d9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_001.png
new file mode 100644
index 0000000..39da0ac
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_002.png
new file mode 100644
index 0000000..d5ada12
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_003.png
new file mode 100644
index 0000000..d4e096c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_004.png
new file mode 100644
index 0000000..468a9b4
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_005.png
new file mode 100644
index 0000000..ea3cd2e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_006.png
new file mode 100644
index 0000000..0652cb0
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_007.png
new file mode 100644
index 0000000..768d2b0
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_008.png
new file mode 100644
index 0000000..1d06a90
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_009.png
new file mode 100644
index 0000000..8a70a80
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_010.png
new file mode 100644
index 0000000..bf9ec7f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_011.png
new file mode 100644
index 0000000..cff07b9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_012.png
new file mode 100644
index 0000000..40f997e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_013.png
new file mode 100644
index 0000000..6ba84ec
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_014.png
new file mode 100644
index 0000000..766610e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_015.png
new file mode 100644
index 0000000..810a029
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_off_pressed_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_off_pressed_qntm_alpha.png
deleted file mode 100644
index fdbbbce..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_off_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_off_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_off_qntm_alpha.png
deleted file mode 100644
index 0ec2ee6..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_on_pressed_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_on_pressed_qntm_alpha.png
deleted file mode 100644
index b46ee1c..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_on_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_on_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_on_qntm_alpha.png
deleted file mode 100644
index 8737156..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_on_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_000.png
new file mode 100644
index 0000000..cbc3833
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_001.png
new file mode 100644
index 0000000..4243895
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_002.png
new file mode 100644
index 0000000..b522d37
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_003.png
new file mode 100644
index 0000000..647b965
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_004.png
new file mode 100644
index 0000000..a317497
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_005.png
new file mode 100644
index 0000000..0e4b25f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_006.png
new file mode 100644
index 0000000..6e279d9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_007.png
new file mode 100644
index 0000000..f0840cc
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_008.png
new file mode 100644
index 0000000..140e9e8
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_009.png
new file mode 100644
index 0000000..5cf8ec5
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_010.png
new file mode 100644
index 0000000..f9624d8
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_011.png
new file mode 100644
index 0000000..899df8c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_012.png
new file mode 100644
index 0000000..6543e1c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_013.png
new file mode 100644
index 0000000..cd758dd
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_014.png
new file mode 100644
index 0000000..72d950c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_015.png
new file mode 100644
index 0000000..07bdbc9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_000.png
new file mode 100644
index 0000000..c9af24b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_001.png
new file mode 100644
index 0000000..01de3f5
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_002.png
new file mode 100644
index 0000000..f428bc5
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_003.png
new file mode 100644
index 0000000..ab5c008
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_004.png
new file mode 100644
index 0000000..5b157cf
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_005.png
new file mode 100644
index 0000000..1210be2
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_006.png
new file mode 100644
index 0000000..e6b4140
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_007.png
new file mode 100644
index 0000000..b678e08
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_008.png
new file mode 100644
index 0000000..6ca2a69
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_009.png
new file mode 100644
index 0000000..7de608e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_010.png
new file mode 100644
index 0000000..b2bbcce
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_011.png
new file mode 100644
index 0000000..6950db3
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_012.png
new file mode 100644
index 0000000..c790756
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_013.png
new file mode 100644
index 0000000..ed5d888
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_014.png
new file mode 100644
index 0000000..81a4a63
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_015.png
new file mode 100644
index 0000000..db1d93a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_000.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_001.png
new file mode 100644
index 0000000..eff3dd0
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_002.png
new file mode 100644
index 0000000..000a23a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_003.png
new file mode 100644
index 0000000..394d661
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_004.png
new file mode 100644
index 0000000..4e7311d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_005.png
new file mode 100644
index 0000000..d9dcf91
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_006.png
new file mode 100644
index 0000000..674142e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_007.png
new file mode 100644
index 0000000..674142e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_008.png
new file mode 100644
index 0000000..9d4026a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_009.png
new file mode 100644
index 0000000..bb4b426
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_010.png
new file mode 100644
index 0000000..a37076d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_011.png
new file mode 100644
index 0000000..a37076d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_012.png
new file mode 100644
index 0000000..a37076d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_013.png
new file mode 100644
index 0000000..a37076d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_014.png
new file mode 100644
index 0000000..a37076d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_000.png
new file mode 100644
index 0000000..22e9951
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_001.png
new file mode 100644
index 0000000..14e6b39
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_002.png
new file mode 100644
index 0000000..86b2c01
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_003.png
new file mode 100644
index 0000000..1c565df
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_004.png
new file mode 100644
index 0000000..b825449
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_005.png
new file mode 100644
index 0000000..170c234
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_006.png
new file mode 100644
index 0000000..5477007
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_007.png
new file mode 100644
index 0000000..eff3dd0
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_008.png
new file mode 100644
index 0000000..e3fd96a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_009.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_010.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_011.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_012.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_013.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_014.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/btn_toggle_indicator_qntm_alpha.9.png
new file mode 100644
index 0000000..0d6a39a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_toggle_indicator_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/btn_toggle_qntm_alpha.9.png
new file mode 100644
index 0000000..f235aed
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_toggle_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_000.png
new file mode 100644
index 0000000..46aa533
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_001.png
new file mode 100644
index 0000000..a749469
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_002.png
new file mode 100644
index 0000000..ef43f00
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_003.png
new file mode 100644
index 0000000..eebddc3
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_004.png
new file mode 100644
index 0000000..44b654d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_005.png
new file mode 100644
index 0000000..6e768c1
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_000.png
new file mode 100644
index 0000000..2ac6dae
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_001.png
new file mode 100644
index 0000000..91c49ce
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_002.png
new file mode 100644
index 0000000..4b4bd1f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_003.png
new file mode 100644
index 0000000..637e596
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_004.png
new file mode 100644
index 0000000..42d4d2a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_005.png
new file mode 100644
index 0000000..995d1b2
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
deleted file mode 100644
index 8d79a13..0000000
--- a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
deleted file mode 100644
index e0e4ef9..0000000
--- a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_track_qntm_alpha.9.png
index 74a259b..a8067cb 100644
--- a/core/res/res/drawable-xxhdpi/switch_track_qntm_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/switch_track_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/text_select_handle_left_qntm_alpha.png b/core/res/res/drawable-xxhdpi/text_select_handle_left_qntm_alpha.png
index 93469a2..75085ce 100644
--- a/core/res/res/drawable-xxhdpi/text_select_handle_left_qntm_alpha.png
+++ b/core/res/res/drawable-xxhdpi/text_select_handle_left_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/text_select_handle_right_qntm_alpha.png b/core/res/res/drawable-xxhdpi/text_select_handle_right_qntm_alpha.png
index b3493e7..e2eb5be 100644
--- a/core/res/res/drawable-xxhdpi/text_select_handle_right_qntm_alpha.png
+++ b/core/res/res/drawable-xxhdpi/text_select_handle_right_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_000.png
new file mode 100644
index 0000000..f0ff1a7
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_001.png
new file mode 100644
index 0000000..b382df3
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_002.png
new file mode 100644
index 0000000..8cb4ce2
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_003.png
new file mode 100644
index 0000000..4db2b01
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_004.png
new file mode 100644
index 0000000..8c4709b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_005.png
new file mode 100644
index 0000000..1ad960a
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_006.png
new file mode 100644
index 0000000..e47cc20
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_007.png
new file mode 100644
index 0000000..c4d0d51
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_008.png
new file mode 100644
index 0000000..915d56a
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_009.png
new file mode 100644
index 0000000..85795cb
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_010.png
new file mode 100644
index 0000000..157fd91
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_011.png
new file mode 100644
index 0000000..9d446de
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_012.png
new file mode 100644
index 0000000..dfac1f0
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_013.png
new file mode 100644
index 0000000..aed6c08
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_014.png
new file mode 100644
index 0000000..1b8bd6b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_015.png
new file mode 100644
index 0000000..5dd0e5b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_000.png
new file mode 100644
index 0000000..5dd0e5b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_001.png
new file mode 100644
index 0000000..1a31ad9
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_002.png
new file mode 100644
index 0000000..63c7f12
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_003.png
new file mode 100644
index 0000000..847dd08
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_004.png
new file mode 100644
index 0000000..b93f3cc
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_005.png
new file mode 100644
index 0000000..1e3dea7
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_006.png
new file mode 100644
index 0000000..5a85238
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_007.png
new file mode 100644
index 0000000..35960ca
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_008.png
new file mode 100644
index 0000000..6db5555
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_009.png
new file mode 100644
index 0000000..a9c5851
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_010.png
new file mode 100644
index 0000000..38465bd
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_011.png
new file mode 100644
index 0000000..15942dc
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_012.png
new file mode 100644
index 0000000..67d0d64
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_013.png
new file mode 100644
index 0000000..69b5c1b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_014.png
new file mode 100644
index 0000000..0e5d331
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_015.png
new file mode 100644
index 0000000..f0ff1a7
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_qntm_alpha.9.png b/core/res/res/drawable-xxxhdpi/btn_qntm_alpha.9.png
new file mode 100644
index 0000000..01eeefe
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_000.png
new file mode 100644
index 0000000..44028af
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_001.png
new file mode 100644
index 0000000..ec13a86
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_002.png
new file mode 100644
index 0000000..43754eb
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_003.png
new file mode 100644
index 0000000..39d1d64
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_004.png
new file mode 100644
index 0000000..f36f883
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_005.png
new file mode 100644
index 0000000..7a4cc5c
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_006.png
new file mode 100644
index 0000000..80a21ec
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_007.png
new file mode 100644
index 0000000..2141104
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_008.png
new file mode 100644
index 0000000..203bd51
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_009.png
new file mode 100644
index 0000000..5df6fc5
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_010.png
new file mode 100644
index 0000000..6d0fced
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_011.png
new file mode 100644
index 0000000..8c0c372
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_012.png
new file mode 100644
index 0000000..4fa6f53
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_013.png
new file mode 100644
index 0000000..d3dbf7d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_014.png
new file mode 100644
index 0000000..4ccf8de
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_015.png
new file mode 100644
index 0000000..adef871
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_000.png
new file mode 100644
index 0000000..adef871
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_001.png
new file mode 100644
index 0000000..9fc3556
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_002.png
new file mode 100644
index 0000000..7f00609
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_003.png
new file mode 100644
index 0000000..e4aa58d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_004.png
new file mode 100644
index 0000000..fe4e4b7
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_005.png
new file mode 100644
index 0000000..86666ca
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_006.png
new file mode 100644
index 0000000..608faaf
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_007.png
new file mode 100644
index 0000000..ec95422
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_008.png
new file mode 100644
index 0000000..76e2754
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_009.png
new file mode 100644
index 0000000..3853eac
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_010.png
new file mode 100644
index 0000000..621aff1
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_011.png
new file mode 100644
index 0000000..d24be2a
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_012.png
new file mode 100644
index 0000000..df33892
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_013.png
new file mode 100644
index 0000000..ff4b818
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_014.png
new file mode 100644
index 0000000..d9793ae
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_015.png
new file mode 100644
index 0000000..44028af
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_000.png
new file mode 100644
index 0000000..8b202c6
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_001.png
new file mode 100644
index 0000000..3b497f3
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_002.png
new file mode 100644
index 0000000..532b6de
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_003.png
new file mode 100644
index 0000000..403b2fe
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_004.png
new file mode 100644
index 0000000..8c5086c
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_005.png
new file mode 100644
index 0000000..d4870f8
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_006.png
new file mode 100644
index 0000000..c05adf5
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_007.png
new file mode 100644
index 0000000..99b2056
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_008.png
new file mode 100644
index 0000000..d839358
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_009.png
new file mode 100644
index 0000000..913f94d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_010.png
new file mode 100644
index 0000000..7f325b3
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_011.png
new file mode 100644
index 0000000..149a9aa
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_012.png
new file mode 100644
index 0000000..95c219e
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_013.png
new file mode 100644
index 0000000..462a128d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_014.png
new file mode 100644
index 0000000..5911d16
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_000.png
new file mode 100644
index 0000000..e0c6d85
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_001.png
new file mode 100644
index 0000000..5679943
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_002.png
new file mode 100644
index 0000000..54b636d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_003.png
new file mode 100644
index 0000000..bf9fac0
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_004.png
new file mode 100644
index 0000000..25d5319
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_005.png
new file mode 100644
index 0000000..d2df595
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_006.png
new file mode 100644
index 0000000..7700bde
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_007.png
new file mode 100644
index 0000000..883f98b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_008.png
new file mode 100644
index 0000000..b3b2108
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_009.png
new file mode 100644
index 0000000..3aad596
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_010.png
new file mode 100644
index 0000000..2017e17
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_011.png
new file mode 100644
index 0000000..1fc2700
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_012.png
new file mode 100644
index 0000000..bb8b0f2
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_013.png
new file mode 100644
index 0000000..66ab8f6
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_014.png
new file mode 100644
index 0000000..e3424db
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-xxxhdpi/btn_toggle_indicator_qntm_alpha.9.png
new file mode 100755
index 0000000..c06740b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_toggle_indicator_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-xxxhdpi/btn_toggle_qntm_alpha.9.png
new file mode 100755
index 0000000..7556167
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_toggle_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_000.png
new file mode 100644
index 0000000..0c8f746
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_001.png
new file mode 100644
index 0000000..5db9deb
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_002.png
new file mode 100644
index 0000000..3aca6d3
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_003.png
new file mode 100644
index 0000000..746c74f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_004.png
new file mode 100644
index 0000000..454a5b2
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_005.png
new file mode 100644
index 0000000..80ad8cc
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_000.png
new file mode 100644
index 0000000..cfd0db4
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_001.png
new file mode 100644
index 0000000..845092f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_002.png
new file mode 100644
index 0000000..0042db4
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_003.png
new file mode 100644
index 0000000..77b2901
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_004.png
new file mode 100644
index 0000000..fb3c238
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_005.png
new file mode 100644
index 0000000..0d28c45
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-xxxhdpi/switch_track_qntm_alpha.9.png
new file mode 100644
index 0000000..fb07f2a
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/switch_track_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_borderless_quantum.xml b/core/res/res/drawable/btn_borderless_quantum.xml
index a8def44..2cd7ed6 100644
--- a/core/res/res/drawable/btn_borderless_quantum.xml
+++ b/core/res/res/drawable/btn_borderless_quantum.xml
@@ -16,21 +16,6 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="?attr/colorControlHighlight">
- <item android:id="@id/mask">
- <inset
- android:insetLeft="4dp"
- android:insetTop="4dp"
- android:insetBottom="4dp"
- android:insetRight="4dp">
- <shape android:shape="rectangle">
- <solid android:color="@color/white" />
- <corners android:radius="2dp" />
- <padding
- android:left="4dp"
- android:top="4dp"
- android:bottom="4dp"
- android:right="4dp" />
- </shape>
- </inset>
- </item>
+ <item android:id="@id/mask"
+ android:drawable="@drawable/btn_qntm_alpha" />
</ripple>
diff --git a/core/res/res/drawable/btn_check_quantum.xml b/core/res/res/drawable/btn_check_quantum.xml
deleted file mode 100644
index 6ceba60..0000000
--- a/core/res/res/drawable/btn_check_quantum.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_checked="true" android:state_pressed="true">
- <bitmap android:src="@drawable/btn_check_on_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:state_checked="true">
- <bitmap android:src="@drawable/btn_check_on_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:state_pressed="true">
- <bitmap android:src="@drawable/btn_check_off_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item>
- <bitmap android:src="@drawable/btn_check_off_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/btn_check_quantum_anim.xml b/core/res/res/drawable/btn_check_quantum_anim.xml
index 96715a4..b16875e 100644
--- a/core/res/res/drawable/btn_check_quantum_anim.xml
+++ b/core/res/res/drawable/btn_check_quantum_anim.xml
@@ -16,88 +16,118 @@
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:state_checked="true">
- <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha"
- android:tint="?attr/colorControlActivated"
- android:alpha="?attr/disabledAlpha" />
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_015" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
</item>
<item android:state_enabled="false">
- <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha"
- android:tint="?attr/colorControlNormal"
- android:alpha="?attr/disabledAlpha" />
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_000" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
</item>
<item android:state_checked="true" android:id="@+id/on">
- <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_015" android:tint="?attr/colorControlActivated" />
</item>
<item android:id="@+id/off">
- <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_000" android:tint="?attr/colorControlNormal" />
</item>
- <transition android:fromId="@+id/off" android:toId="@+id/on" android:reversible="true">
+ <transition android:fromId="@+id/off" android:toId="@+id/on">
<animation-list>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_000" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00001_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_001" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00002_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_002" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00003_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_003" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00004_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_004" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00005_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_005" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00006_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_006" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00007_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_007" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00008_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_008" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00009_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_009" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00010_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_010" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00011_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_011" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00012_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_012" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00013_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_013" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00014_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_014" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_qntm_015" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+ <transition android:fromId="@+id/on" android:toId="@+id/off">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_000" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_001" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_002" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_003" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_004" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_005" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_006" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_007" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_008" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_009" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_010" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_011" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_012" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_013" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_014" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_qntm_015" android:tint="?attr/colorControlActivated" />
</item>
</animation-list>
</transition>
diff --git a/core/res/res/drawable/btn_default_quantum.xml b/core/res/res/drawable/btn_default_quantum.xml
index 63473a4..61193fe 100644
--- a/core/res/res/drawable/btn_default_quantum.xml
+++ b/core/res/res/drawable/btn_default_quantum.xml
@@ -17,20 +17,7 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="?attr/colorControlHighlight">
<item>
- <inset
- android:insetLeft="4dp"
- android:insetTop="4dp"
- android:insetBottom="4dp"
- android:insetRight="4dp">
- <shape android:shape="rectangle">
- <solid android:color="?attr/colorButtonNormal" />
- <corners android:radius="2dp" />
- <padding
- android:left="4dp"
- android:top="4dp"
- android:bottom="4dp"
- android:right="4dp" />
- </shape>
- </inset>
+ <nine-patch android:src="@drawable/btn_qntm_alpha"
+ android:tint="?attr/colorButtonNormal" />
</item>
</ripple>
diff --git a/core/res/res/drawable/btn_radio_quantum.xml b/core/res/res/drawable/btn_radio_quantum.xml
deleted file mode 100644
index 0f9ebce..0000000
--- a/core/res/res/drawable/btn_radio_quantum.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:state_checked="true">
- <bitmap android:src="@drawable/btn_radio_on_qntm_alpha"
- android:tint="?attr/colorControlNormal"
- android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_enabled="false">
- <bitmap android:src="@drawable/btn_radio_off_qntm_alpha"
- android:tint="?attr/colorControlNormal"
- android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_checked="true">
- <bitmap android:src="@drawable/btn_radio_on_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item>
- <bitmap android:src="@drawable/btn_radio_off_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/btn_radio_quantum_anim.xml b/core/res/res/drawable/btn_radio_quantum_anim.xml
index 5068b7a..cd9b518 100644
--- a/core/res/res/drawable/btn_radio_quantum_anim.xml
+++ b/core/res/res/drawable/btn_radio_quantum_anim.xml
@@ -16,88 +16,118 @@
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:state_checked="true">
- <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha"
- android:tint="?attr/colorControlActivated"
- android:alpha="?attr/disabledAlpha" />
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_015" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
</item>
<item android:state_enabled="false">
- <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha"
- android:tint="?attr/colorControlNormal"
- android:alpha="?attr/disabledAlpha" />
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_000" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
</item>
<item android:state_checked="true" android:id="@+id/on">
- <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_015" android:tint="?attr/colorControlActivated" />
</item>
<item android:id="@+id/off">
- <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_000" android:tint="?attr/colorControlNormal" />
</item>
- <transition android:fromId="@+id/off" android:toId="@+id/on" android:reversible="true">
+ <transition android:fromId="@+id/off" android:toId="@+id/on">
<animation-list>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_000" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00001_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_001" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00002_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_002" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00003_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_003" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00004_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_004" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00005_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_005" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00006_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_006" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00007_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_007" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00008_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_008" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00009_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_009" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00010_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_010" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00011_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_011" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00012_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_012" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00013_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_013" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00014_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_014" android:tint="?attr/colorControlActivated" />
</item>
- <item android:duration="33">
- <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_qntm_015" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+ <transition android:fromId="@+id/on" android:toId="@+id/off">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_000" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_001" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_002" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_003" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_004" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_005" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_006" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_007" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_008" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_009" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_010" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_011" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_012" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_013" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_014" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_qntm_015" android:tint="?attr/colorControlActivated" />
</item>
</animation-list>
</transition>
diff --git a/core/res/res/drawable/btn_toggle_quantum.xml b/core/res/res/drawable/btn_toggle_quantum.xml
new file mode 100644
index 0000000..e235598
--- /dev/null
+++ b/core/res/res/drawable/btn_toggle_quantum.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="4dp"
+ android:insetTop="4dp"
+ android:insetBottom="4dp"
+ android:insetRight="4dp">
+ <layer-list android:paddingMode="stack">
+ <item>
+ <ripple android:tint="?attr/colorControlHighlight">
+ <item>
+ <nine-patch android:src="@drawable/btn_toggle_qntm_alpha"
+ android:tint="?attr/colorButtonNormal" />
+ </item>
+ </ripple>
+ </item>
+ <item>
+ <selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="false">
+ <nine-patch android:src="@drawable/btn_toggle_indicator_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:state_checked="true">
+ <nine-patch android:src="@drawable/btn_toggle_indicator_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ </selector>
+ </item>
+ </layer-list>
+</inset>
diff --git a/core/res/res/drawable/ic_audio_ring_notif.xml b/core/res/res/drawable/ic_audio_ring_notif.xml
index 247d1b4..b52db5c 100644
--- a/core/res/res/drawable/ic_audio_ring_notif.xml
+++ b/core/res/res/drawable/ic_audio_ring_notif.xml
@@ -1,23 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
- * Copyright 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.
- */
--->
+Copyright (C) 2014 The Android Open Source Project
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_audio_ring_notif_am_alpha"
- android:autoMirrored="true"
- android:tint="?attr/colorControlNormal" />
+ 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="32dp"
+ android:height="32dp"/>
+
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="#8A000000"
+ android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_audio_ring_notif_mute.xml b/core/res/res/drawable/ic_audio_ring_notif_mute.xml
index 72aaa9d..8d7d6cb 100644
--- a/core/res/res/drawable/ic_audio_ring_notif_mute.xml
+++ b/core/res/res/drawable/ic_audio_ring_notif_mute.xml
@@ -1,23 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
- * Copyright 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.
- */
--->
+Copyright (C) 2014 The Android Open Source Project
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_audio_ring_notif_mute_am_alpha"
- android:autoMirrored="true"
- android:tint="?attr/colorControlNormal" />
+ 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="32dp"
+ android:height="32dp"/>
+
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="#8A000000"
+ android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7C9.5,4.3 9.0,4.5 8.6,4.7l9.4,9.4L18.0,10.5zM17.7,19.0l2.0,2.0l1.3,-1.3L4.3,3.0L3.0,4.3l2.9,2.9C5.3,8.2 5.0,9.3 5.0,10.5L5.0,16.0l-2.0,2.0l0.0,1.0L17.7,19.0z" />
+</vector>
diff --git a/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml b/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml
index 9e31aba..2f1d940 100644
--- a/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml
+++ b/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml
@@ -1,23 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
- * Copyright 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.
- */
--->
+Copyright (C) 2014 The Android Open Source Project
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_audio_ring_notif_vibrate_am_alpha"
- android:autoMirrored="true"
- android:tint="?attr/colorControlNormal" />
+ 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="32dp"
+ android:height="32dp"/>
+
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="#8A000000"
+ android:pathData="M0.0,15.0l2.0,0.0L2.0,9.0L0.0,9.0L0.0,15.0zM3.0,17.0l2.0,0.0L5.0,7.0L3.0,7.0L3.0,17.0zM22.0,9.0l0.0,6.0l2.0,0.0L24.0,9.0L22.0,9.0zM19.0,17.0l2.0,0.0L21.0,7.0l-2.0,0.0L19.0,17.0zM16.5,3.0l-9.0,0.0C6.7,3.0 6.0,3.7 6.0,4.5l0.0,15.0C6.0,20.3 6.7,21.0 7.5,21.0l9.0,0.0c0.8,0.0 1.5,-0.7 1.5,-1.5l0.0,-15.0C18.0,3.7 17.3,3.0 16.5,3.0zM16.0,19.0L8.0,19.0L8.0,5.0l8.0,0.0L16.0,19.0z"/>
+</vector>
diff --git a/core/res/res/drawable/scrubber_control_quantum_anim.xml b/core/res/res/drawable/scrubber_control_quantum_anim.xml
new file mode 100644
index 0000000..87d3ae9
--- /dev/null
+++ b/core/res/res/drawable/scrubber_control_quantum_anim.xml
@@ -0,0 +1,74 @@
+<?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.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
+ <item android:state_enabled="false" android:state_pressed="true">
+ <bitmap android:src="@drawable/scrubber_control_off_qntm_alpha" android:gravity="center" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_enabled="false">
+ <bitmap android:src="@drawable/scrubber_control_off_qntm_alpha" android:gravity="center" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_pressed="true" android:id="@+id/pressed">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:id="@+id/not_pressed">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <transition android:fromId="@+id/not_pressed" android:toId="@+id/pressed">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+ <transition android:fromId="@+id/pressed" android:toId="@+id/not_pressed">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+</animated-selector>
diff --git a/core/res/res/drawable/switch_inner_quantum.xml b/core/res/res/drawable/switch_inner_quantum.xml
deleted file mode 100644
index 856895e..0000000
--- a/core/res/res/drawable/switch_inner_quantum.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:state_checked="true">
- <nine-patch android:src="@drawable/switch_on_qntm_alpha"
- android:tint="?attr/colorControlNormal"
- android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_enabled="false">
- <nine-patch android:src="@drawable/switch_off_qntm_alpha"
- android:tint="?attr/colorControlNormal"
- android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_checked="true">
- <nine-patch android:src="@drawable/switch_on_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item>
- <nine-patch android:src="@drawable/switch_off_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/switch_thumb_quantum_anim.xml b/core/res/res/drawable/switch_thumb_quantum_anim.xml
new file mode 100644
index 0000000..1984d47
--- /dev/null
+++ b/core/res/res/drawable/switch_thumb_quantum_anim.xml
@@ -0,0 +1,128 @@
+<?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.
+-->
+
+<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_qntm_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_qntm_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_qntm_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:id="@+id/off">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_000" android:gravity="center" android:tint="?attr/colorControlNormal" />
+ </item>
+ <transition android:fromId="@+id/off" android:toId="@+id/on">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_006" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_007" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_008" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_009" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_010" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_011" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_012" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_013" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_qntm_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+ <transition android:fromId="@+id/on" android:toId="@+id/off">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_006" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_007" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_008" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_009" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_010" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_011" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_012" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_013" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_qntm_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+</animated-selector>
diff --git a/core/res/res/drawable/switch_track_quantum.xml b/core/res/res/drawable/switch_track_quantum.xml
index 8c4e6b71..3651a0a 100644
--- a/core/res/res/drawable/switch_track_quantum.xml
+++ b/core/res/res/drawable/switch_track_quantum.xml
@@ -15,6 +15,16 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false" android:state_checked="true">
+ <nine-patch android:src="@drawable/switch_track_qntm_alpha"
+ android:tint="?attr/colorControlActivated"
+ android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_enabled="false">
+ <nine-patch android:src="@drawable/switch_track_qntm_alpha"
+ android:tint="?attr/colorControlNormal"
+ android:alpha="?attr/disabledAlpha" />
+ </item>
<item android:state_checked="true">
<nine-patch android:src="@drawable/switch_track_qntm_alpha"
android:tint="?attr/colorControlActivated" />
diff --git a/core/res/res/layout/global_actions_silent_mode.xml b/core/res/res/layout/global_actions_silent_mode.xml
index 79401af..a358623 100644
--- a/core/res/res/layout/global_actions_silent_mode.xml
+++ b/core/res/res/layout/global_actions_silent_mode.xml
@@ -37,7 +37,7 @@
android:layout_marginEnd="8dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
- android:src="@drawable/ic_audio_vol_mute"
+ android:src="@drawable/ic_audio_ring_notif_mute"
android:scaleType="center"
android:duplicateParentState="true"
android:background="@drawable/silent_mode_indicator"
@@ -94,7 +94,7 @@
android:layout_marginEnd="8dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
- android:src="@drawable/ic_audio_vol"
+ android:src="@drawable/ic_audio_ring_notif"
android:scaleType="center"
android:duplicateParentState="true"
android:background="@drawable/silent_mode_indicator"
diff --git a/core/res/res/layout/volume_adjust.xml b/core/res/res/layout/volume_adjust.xml
deleted file mode 100644
index 3ad1f23..0000000
--- a/core/res/res/layout/volume_adjust.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/visible_panel"
- android:orientation="horizontal"
- android:layout_width="300dp"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:id="@+id/slider_group"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical">
- <!-- Sliders go here -->
- </LinearLayout>
-
- <ImageView
- android:id="@+id/expand_button_divider"
- android:src="?attr/dividerVertical"
- android:layout_width="wrap_content"
- android:layout_height="32dip"
- android:scaleType="fitXY"
- android:layout_gravity="top"
- android:layout_marginTop="16dip"
- android:layout_marginBottom="16dip" />
-
- <ImageView
- android:id="@+id/expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:padding="16dip"
- android:background="?attr/selectableItemBackground"
- android:src="@drawable/ic_sysbar_quicksettings" />
-
-</LinearLayout>
diff --git a/core/res/res/layout/volume_adjust_item.xml b/core/res/res/layout/volume_adjust_item.xml
deleted file mode 100644
index 57cecf4..0000000
--- a/core/res/res/layout/volume_adjust_item.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="80dip"
- android:orientation="horizontal"
- android:layout_marginTop="8dip"
- android:layout_marginBottom="8dip"
- android:gravity="start|center_vertical">
-
- <ImageView
- android:id="@+id/stream_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="16dip"
- android:background="?attr/selectableItemBackground"
- android:contentDescription="@null" />
-
- <SeekBar
- style="?android:attr/seekBarStyle"
- android:id="@+id/seekbar"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:padding="16dip"
- android:layout_marginEnd="16dip" />
-
-</LinearLayout>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index df59abe..b17b8c6 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -267,7 +267,7 @@
<string name="permdesc_statusBarService" msgid="716113660795976060">"Tillader, at appen er statusbjælken."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"udvid/skjul statuslinje"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Tillader, at appen kan udvide og skjule statusbjælken."</string>
- <string name="permlab_install_shortcut" msgid="4279070216371564234">"installer genveje"</string>
+ <string name="permlab_install_shortcut" msgid="4279070216371564234">"installere genveje"</string>
<string name="permdesc_install_shortcut" msgid="8341295916286736996">"Tillader, at en applikation føjer genveje til startskærmen uden brugerindgriben."</string>
<string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"afinstaller genveje"</string>
<string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Tillader, at applikationen fjerner genveje på startskærmen uden brugerindgriben."</string>
@@ -281,7 +281,7 @@
<string name="permdesc_receiveEmergencyBroadcast" msgid="848524070262431974">"Tillader, at appen kan modtage og behandle nødtransmissioner. Denne tilladelse er kun tilgængelig for systemapps."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"læse Cell Broadcast-beskeder"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Tillader, at appen læser Cell Broadcast-beskeder, der modtages af din enhed. I nogle områder sendes der Cell Broadcast-beskeder for at advare om nødsituationer. Ondsindede apps kan forstyrre ydelsen eller driften af din enhed, når der modtages en Cell Broadcast-besked om en nødsituation."</string>
- <string name="permlab_sendSms" msgid="5600830612147671529">"send sms-beskeder"</string>
+ <string name="permlab_sendSms" msgid="5600830612147671529">"sende sms-beskeder"</string>
<string name="permdesc_sendSms" msgid="7094729298204937667">"Tillader, at appen kan sende sms-beskeder. Dette kan resultere i uventede opkrævninger. Skadelige apps kan koste dig penge ved at sende beskeder uden din bekræftelse."</string>
<string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"send hændelser, hvor der skal svares pr. besked"</string>
<string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Tillader, at appen kan sende anmodninger til andre apps til beskeder for at håndtere hændelser, hvor der skal svares pr. besked."</string>
@@ -295,7 +295,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Tillader, at appen kan modtage og behandle WAP-beskeder. Denne tilladelse omfatter muligheden for at overvåge eller slette de beskeder, der sendes til dig, uden at vise dem til dig."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"hente kørende apps"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Tillader, at appen kan hente oplysninger om nuværende og seneste opgaver. Med denne tilladelse kan appen finde oplysninger om, hvilke applikationer der bruges på enheden."</string>
- <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"kommuniker på tværs af brugere"</string>
+ <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"kommunikere på tværs af brugere"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Tillader, at appen udfører handlinger på tværs af forskellige brugere på enheden. Ondsindede apps kan bruge dette til at krænke beskyttelsen mellem brugere."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"fuld licens til at kommunikere på tværs af brugere"</string>
<string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"Tillader alle mulige former for kommunikation på tværs af brugere."</string>
@@ -325,7 +325,7 @@
<string name="permdesc_forceStopPackages" msgid="5253157296183940812">"Tillader, at appen kan tvinge andre apps til at stoppe."</string>
<string name="permlab_forceBack" msgid="652935204072584616">"tvinge appen til at lukke"</string>
<string name="permdesc_forceBack" msgid="3892295830419513623">"Tillader, at appen kan tvinge enhver aktivitet i forgrunden til at lukke og gå tilbage. Bør aldrig være nødvendigt til almindelige apps."</string>
- <string name="permlab_dump" msgid="1681799862438954752">"hent intern systemtilstand"</string>
+ <string name="permlab_dump" msgid="1681799862438954752">"hente intern systemtilstand"</string>
<string name="permdesc_dump" msgid="1778299088692290329">"Tillader, at appen kan hente systemets interne tilstand. Ondsindede apps kan hente en lang række fortrolige og beskyttede oplysninger, som de normalt aldrig ville have brug for."</string>
<string name="permlab_retrieve_window_content" msgid="8022588608994589938">"hente skærmindhold"</string>
<string name="permdesc_retrieve_window_content" msgid="3193269069469700265">"Tillader, at appen kan hente indholdet i det aktive vindue. Ondsindede apps kan hente al indholdet i vinduet og undersøge al dens tekst med undtagelse af adgangskoder."</string>
@@ -359,7 +359,7 @@
<string name="permdesc_batteryStats" msgid="5897346582882915114">"Tillader, at en applikation læser de aktuelle data for batteriforbruget. Kan tillade, at applikationen henter detaljerede oplysninger om, hvilke apps du bruger."</string>
<string name="permlab_updateBatteryStats" msgid="3719689764536379557">"rediger batteristatistikker"</string>
<string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Tillader, at appen kan ændre indsamlede batteristatistikker. Anvendes ikke af normale apps."</string>
- <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"hent statistikker for handlinger i appen"</string>
+ <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"hente statistikker om handlinger i appen"</string>
<string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Tillader, at appen indhenter statistikker for handlinger i applikationen. Denne handling bruges ikke i almindelige apps."</string>
<string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"lav ændringer i statistik for handlinger i appen"</string>
<string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Tillader, at appen kan ændre indsamlede statistikker for handlinger i applikationen. Dette kan ikke bruges af almindelige apps."</string>
@@ -474,7 +474,7 @@
<string name="permlab_writeContacts" msgid="5107492086416793544">"ændre dine kontaktpersoner"</string>
<string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Tillader, at appen kan ændre data om de kontaktpersoner, der er gemt på din tablet, f.eks. hvor ofte du har ringet til dem, sendt dem en e-mail eller på anden måde kommunikeret med bestemte kontaktpersoner. Med denne tilladelse kan apps slette kontaktoplysninger."</string>
<string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Tillader, at appen kan ændre data om de kontaktpersoner, der er gemt på din telefon, f.eks. hvor ofte du har ringet til dem, sendt en e-mail til dem eller på anden måde kommunikeret med bestemte kontaktpersoner. Med denne tilladelse kan apps slette kontaktoplysninger."</string>
- <string name="permlab_readCallLog" msgid="3478133184624102739">"læs opkaldsliste"</string>
+ <string name="permlab_readCallLog" msgid="3478133184624102739">"læse opkaldsliste"</string>
<string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Tillader, at appen kan læse din tablets opkaldsliste, f.eks. data om indgående og udgående opkald. Med denne tilladelse kan apps gemme dine opkaldslistedata, og skadelige apps kan dele opkaldslistedata uden din viden."</string>
<string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Tillader, at appen kan læse telefonens opkaldsliste, f.eks. data om indgående og udgående opkald. Med denne tilladelse kan apps gemme dine opkaldslistedata, og skadelige apps kan dele disse opkaldslistedata uden din viden."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"skriv opkaldsliste"</string>
@@ -486,9 +486,9 @@
<string name="permdesc_writeProfile" product="default" msgid="5552084294598465899">"Tillader, at appen kan ændre eller tilføje oplysninger i din personlige profil, der er gemt på din enhed, f.eks. dit navn eller dine kontaktoplysninger. Dette betyder, at andre apps kan identificere dig og sende profiloplysninger til andre."</string>
<string name="permlab_bodySensors" msgid="4871091374767171066">"kropssensorer (f.eks. pulsmålere)"</string>
<string name="permdesc_bodySensors" product="default" msgid="2998865085124153531">"Tillader, at appen får adgang til data fra sensorer, du bruger til at måle, hvad der sker inde i din krop, f.eks. din puls."</string>
- <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"læs din sociale strøm"</string>
+ <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"læse din sociale strøm"</string>
<string name="permdesc_readSocialStream" product="default" msgid="4255706027172050872">"Tillader, at appen kan få adgang til og synkronisere sociale opdateringer fra dig og dine venner. Vær forsigtig, når du deler oplysninger – med denne tilladelse kan appen læse kommunikation mellem dig og dine venner på sociale netværk, uanset fortrolighed. Bemærk! Denne tilladelse håndhæves muligvis ikke på alle sociale netværk."</string>
- <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"skriv i din sociale strøm"</string>
+ <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"skrive i din sociale strøm"</string>
<string name="permdesc_writeSocialStream" product="default" msgid="3086557552204114849">"Tillader, at appen kan vise sociale opdateringer fra dine venner. Vær forsigtig, når du deler oplysninger – med denne tilladelse kan appen producere meddelelser, der kan synes at komme fra en ven. Bemærk! Denne tilladelse håndhæves muligvis ikke på alle sociale netværk."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"læse kalenderbegivenheder og fortrolige oplysninger"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tillader, at appen kan læse alle de kalenderbegivenheder, der er gemt på din tablet, f.eks. venners eller kollegers. Med denne tilladelse kan appen dele eller gemme dine kalenderdata, uanset fortrolighed eller følsomhed."</string>
@@ -526,7 +526,7 @@
<string name="permdesc_captureSecureVideoOutput" msgid="2779793064709350289">"Tillader, at appen opfanger og omdirigerer et sikkert videooutput."</string>
<string name="permlab_mediaContentControl" msgid="8749790560720562511">"kontrollér medieafspilning og metadataadgang"</string>
<string name="permdesc_mediaContentControl" msgid="1637478200272062">"Tillader, at appen styrer medieafspilning og får adgang til medieoplysninger (titel, forfatter...)."</string>
- <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"skift dine lydindstillinger"</string>
+ <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"skifte dine lydindstillinger"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Tillader, at appen kan ændre globale lydindstillinger, som f.eks. lydstyrke og hvilken højttaler der bruges til output."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"optage lyd"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Tillader, at appen kan optage lyd med mikrofonen. Med denne tilladelse kan appen til enhver tid optage lyd uden din bekræftelse."</string>
@@ -632,7 +632,7 @@
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Tillader, at appen kan oprette netværkssockets og bruge tilpassede netværksprotokoller. Browseren og andre applikationer indeholder midler til at sende data til internettet, så med denne tilladelse er der ingen forpligtelse til at sende data til internettet."</string>
<string name="permlab_writeApnSettings" msgid="505660159675751896">"ændre/opfange netværksindstillinger og trafik"</string>
<string name="permdesc_writeApnSettings" msgid="5333798886412714193">"Tillader, at appen kan ændre netværksindstillinger og opsnappe og inspicere al netværkstrafik, f.eks. for at ændre proxy og port for et adgangspunkt. Ondsindede apps kan overvåge, omdirigere eller ændre netværkspakker uden din viden."</string>
- <string name="permlab_changeNetworkState" msgid="958884291454327309">"skift netværksforbindelse"</string>
+ <string name="permlab_changeNetworkState" msgid="958884291454327309">"skifte netværksforbindelse"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Tillader, at appen kan ændre netværksforbindelsens tilstand."</string>
<string name="permlab_changeTetherState" msgid="5952584964373017960">"skifte forbindelse til netdeling"</string>
<string name="permdesc_changeTetherState" msgid="1524441344412319780">"Tillader, at appen kan ændre tilstand for en netværksforbindelse via netdeling."</string>
@@ -672,9 +672,9 @@
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Tillader, at en app kan ændre synkroniseringsindstillingerne for en konto. Denne tilladelse kan f.eks. anvendes til at aktivere synkronisering af appen Personer med en konto."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"læse synkroniseringsstatistikker"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Tillader, at en app kan læse synkroniseringsstatistikkerne for en konto, f.eks. historikken for synkroniserede begivenheder og hvor meget data der synkroniseres."</string>
- <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"læs abonnerede feeds"</string>
+ <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"læse feeds, jeg abonnerer på"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Tillader, at appen kan hente oplysninger om de feeds, der synkroniseres."</string>
- <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"skriv abonnerede feeds"</string>
+ <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"skrive feeds, som jeg abonnerer på"</string>
<string name="permdesc_subscribedFeedsWrite" msgid="6928930188826089413">"Tillader, at appen kan ændre dine synkroniserede feeds. Ondsindede apps kan ændre dine synkroniserede feeds."</string>
<string name="permlab_readDictionary" msgid="4107101525746035718">"læse termer, som du har føjet til ordbogen"</string>
<string name="permdesc_readDictionary" msgid="659614600338904243">"Tillader, at appen kan læse alle ord, navne og sætninger, som brugeren har gemt i brugerordbogen."</string>
@@ -995,7 +995,7 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Tillader, at appen kan ændre browserens historik eller de bogmærker, der er gemt på din telefon. Dette kan give appen tilladelse til at slette eller ændre browserdata. Bemærk! Denne tilladelse håndhæves muligvis ikke af tredjepartsbrowsere eller andre applikationer med websøgningsfunktioner."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"indstille en alarm"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Tillader, at appen kan indstille en alarm i en installeret alarmapp. Nogle alarmapps har muligvis ikke denne funktion."</string>
- <string name="permlab_addVoicemail" msgid="5525660026090959044">"tilføj telefonsvarer"</string>
+ <string name="permlab_addVoicemail" msgid="5525660026090959044">"tilføje telefonsvarer"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Tillader, at appen kan tilføje beskeder på din telefonsvarer."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"skifte tilladelser til geografisk placering i Browser"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Tillader, at appen kan ændre browserens tilladelser angående geografisk placering. Ondsindede apps kan benytte dette til at sende oplysninger om placering til vilkårlige websites."</string>
@@ -1275,7 +1275,7 @@
<string name="date_picker_dialog_title" msgid="5879450659453782278">"Angiv dato"</string>
<string name="date_time_set" msgid="5777075614321087758">"Angiv"</string>
<string name="date_time_done" msgid="2507683751759308828">"Udført"</string>
- <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"NYHED! "</font></string>
+ <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"NY: "</font></string>
<string name="perms_description_app" msgid="5139836143293299417">"Leveret af <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
<string name="no_permissions" msgid="7283357728219338112">"Der kræves ingen tilladelser"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"dette kan koste dig penge"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 989eec6..d0561b9 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1471,8 +1471,8 @@
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
<string name="activitychooserview_choose_application" msgid="2125168057199941199">"Επιλέξτε κάποια εφαρμογή"</string>
<string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"Δεν ήταν δυνατή η εκκίνηση του <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
- <string name="shareactionprovider_share_with" msgid="806688056141131819">"Κοινοποίηση με"</string>
- <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Κοινοποίηση με <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+ <string name="shareactionprovider_share_with" msgid="806688056141131819">"Κοινή χρήση με"</string>
+ <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Κοινή χρήση με <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
<string name="content_description_sliding_handle" msgid="415975056159262248">"Στοιχείο χειρισμού με δυνατότητα ολίσθησης. Αγγίξτε και πατήστε παρατεταμένα."</string>
<string name="description_target_unlock_tablet" msgid="3833195335629795055">"Σύρετε για ξεκλείδωμα."</string>
<string name="keyboard_headset_required_to_hear_password" msgid="7011927352267668657">"Συνδέστε ακουστικά για να ακούσετε τα πλήκτρα του κωδικού πρόσβασης να εκφωνούνται."</string>
@@ -1516,7 +1516,7 @@
<string name="sha1_fingerprint" msgid="7930330235269404581">"Αποτύπωμα SHA-1"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Εμφάνιση όλων"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Επιλογή δραστηριότητας"</string>
- <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινοποίηση με"</string>
+ <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινή χρήση με"</string>
<string name="list_delimeter" msgid="3975117572185494152">", "</string>
<string name="sending" msgid="3245653681008218030">"Γίνεται αποστολή…"</string>
<string name="launchBrowserDefault" msgid="2057951947297614725">"Εκκίνηση προγράμματος περιήγησης;"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c8eca25..a7cd9a7 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -503,7 +503,7 @@
<string name="permlab_installLocationProvider" msgid="6578101199825193873">"autoriser l\'installation d\'un fournisseur de services de localisation"</string>
<string name="permdesc_installLocationProvider" msgid="9066146120470591509">"Permet de créer des sources de localisation fictives à des fins de tests ou pour installer un nouveau fournisseur de position. L\'application peut ainsi modifier la position et/ou l\'état renvoyé par d\'autres sources de localisation telles que le GPS ou les fournisseurs de position."</string>
<string name="permlab_accessFineLocation" msgid="1191898061965273372">"position précise (GPS et réseau)"</string>
- <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permet à l\'application d\'obtenir votre position exacte à l\'aide du récepteur satellite GPS ou des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puissent déterminer où vous vous trouvez, le cas échéant. Cette autorisation peut entraîner une utilisation accrue de la batterie."</string>
+ <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permet à l\'application d\'obtenir votre position exacte à l\'aide du récepteur satellite GPS ou des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puisse déterminer où vous vous trouvez, le cas échéant. Cette autorisation peut entraîner une utilisation accrue de la batterie."</string>
<string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"position approximative (réseau)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permet à l\'application d\'obtenir votre position approximative. Celle-ci est fournie par des services de localisation sur la base des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puisse déterminer où vous vous trouvez de façon approximative, le cas échéant."</string>
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"Accès à SurfaceFlinger"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 000fb3a..2adaaf1 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -496,7 +496,7 @@
<string name="permlab_writeCalendar" msgid="8438874755193825647">"dodajte ili izmijenite kalendarske događaje i pošaljite e-poštu gostima bez znanja vlasnika"</string>
<string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Aplikaciji omogućuje dodavanje, uklanjanje i promjenu događaja koje možete izmijeniti na tabletnom računalu, uključujući one od vaših prijatelja ili suradnika. To aplikaciji može omogućiti slanje poruka koje izgledaju kao da dolaze od vlasnika kalendara ili izmjenu događaja bez znanja vlasnika."</string>
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Aplikaciji omogućuje dodavanje, uklanjanje i promjenu događaja koje možete izmijeniti na telefonu, uključujući one od vaših prijatelja ili suradnika. To aplikaciji može omogućiti slanje poruka koje izgledaju kao da dolaze od vlasnika kalendara ili izmjenu događaja bez znanja vlasnika."</string>
- <string name="permlab_accessMockLocation" msgid="8688334974036823330">"omogući testiranje izvora lokacije"</string>
+ <string name="permlab_accessMockLocation" msgid="8688334974036823330">"omogućeno testiranje izvora lokacije"</string>
<string name="permdesc_accessMockLocation" msgid="5808711039482051824">"Stvaranje lažnih izvora lokacije radi testiranja ili za instaliranje novog pružatelja usluga lokacije. To aplikaciji omogućuje zaobilaženje lokacije i/ili statusa koji vraćaju drugi izvori lokacije, primjerice GPS ili pružatelji usluga lokacije."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pristup dodatnim naredbama davatelja lokacije"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="5945166642335800763">"Aplikaciji omogućuje pristup dodatnim naredbama za pružatelja usluga lokacije. To aplikaciji može omogućiti ometanje rada GPS-a ili drugih izvora lokacije."</string>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 8f83ab2..3180e58 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -19,13 +19,9 @@
-->
<resources>
<!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_width">200dp</dimen>
+ <dimen name="thumbnail_width">512dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_height">177dp</dimen>
- <!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="recents_thumbnail_width">512dp</dimen>
- <!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="recents_thumbnail_height">512dp</dimen>
+ <dimen name="thumbnail_height">512dp</dimen>
<!-- The maximum number of action buttons that should be permitted within
an action bar/action mode. This will be used to determine how many
showAsAction="ifRoom" items can fit. "always" items can override this. -->
diff --git a/core/res/res/values-sw720dp/dimens.xml b/core/res/res/values-sw720dp/dimens.xml
index 040bb5b..21235ec 100644
--- a/core/res/res/values-sw720dp/dimens.xml
+++ b/core/res/res/values-sw720dp/dimens.xml
@@ -35,13 +35,9 @@
<item type="dimen" name="dialog_fixed_height_minor">90%</item>
<!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_width">230dp</dimen>
+ <dimen name="thumbnail_width">640dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_height">135dp</dimen>
- <!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="recents_thumbnail_width">512dp</dimen>
- <!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="recents_thumbnail_height">512dp</dimen>
+ <dimen name="thumbnail_height">640dp</dimen>
<!-- Preference activity, vertical padding for the header list -->
<dimen name="preference_screen_header_vertical_padding">32dp</dimen>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index c05dfca..9eb6f74 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4057,6 +4057,9 @@
The default is one.
See {@link android.widget.GridLayout.Spec}. -->
<attr name="layout_rowSpan" format="integer" min="1" />
+ <!-- The relative proportion of horizontal space that should be allocated to this view
+ during excess space distribution. -->
+ <attr name="layout_rowWeight" format="float" />
<!-- The column boundary delimiting the left of the group of cells
occupied by this view. -->
<attr name="layout_column" />
@@ -4065,6 +4068,9 @@
The default is one.
See {@link android.widget.GridLayout.Spec}. -->
<attr name="layout_columnSpan" format="integer" min="1" />
+ <!-- The relative proportion of vertical space that should be allocated to this view
+ during excess space distribution. -->
+ <attr name="layout_columnWeight" format="float" />
<!-- Gravity specifies how a component should be placed in its group of cells.
The default is LEFT | BASELINE.
See {@link android.widget.GridLayout.LayoutParams#setGravity(int)}. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b1f256e..acfbe2d 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -885,41 +885,57 @@
be passed a persistable Bundle in their Intent.extras. -->
<attr name="persistable" format="boolean" />
- <!-- Specify whether this activity should always be launched in doc-centric mode. For
- values other than <code>none</code> the activity must be defined with
- {@link android.R.attr#launchMode} <code>standard</code> or <code>singleTop</code>.
- This attribute can be overridden by {@link
- android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}.
+ <!-- This attribute specifies that an activity shall become the root activity of a
+ new task each time it is launched. Using this attribute permits the user to
+ have multiple documents from the same applications appear in the recent tasks list.
- <p>If this attribute is not specified, <code>none</code> will be used.
- Note that this launch behavior can be changed in some ways at runtime
- through the {@link android.content.Intent} flags
- {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}. -->
+ <p>Such a document is any kind of item for which an application may want to
+ maintain multiple simultaneous instances. Examples might be text files, web
+ pages, spreadsheets, or emails. Each such document will be in a separate
+ task in the recent taskss list.
+
+ <p>This attribute is equivalent to adding the flag {@link
+ android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} to every Intent used to launch
+ the activity.
+
+ <p>The documentLaunchMode attribute may be assigned one of three values, "none",
+ "intoExisting" and "always", described in detail below. For values other than
+ <code>none</code> the activity must be defined with
+ {@link android.R.attr#launchMode} <code>standard</code> or <code>singleTop</code>.
+ If this attribute is not specified, <code>none</code> will be used.
+ Note that <code>none</code> can be overridden at run time if the Intent used
+ to launch it contains the flag {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}.
+ Similarly <code>intoExisting</code> will be overridden by the flag
+ {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} combined with
+ {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}. -->
<attr name="documentLaunchMode">
<!-- The default mode, which will create a new task only when
{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK
Intent.FLAG_ACTIVITY_NEW_TASK} is set. -->
<enum name="none" value="0" />
- <!-- All tasks will be searched for a matching Intent. If one is found
- That task will cleared and restarted with the root activity receiving a call
+ <!-- All tasks will be searched for one whose base Intent's ComponentName and
+ data URI match those of the launching Intent. If such a task is found
+ that task will be cleared and restarted with the root activity receiving a call
to {@link android.app.Activity#onNewIntent Activity.onNewIntent}. If no
such task is found a new task will be created.
- This is the equivalent of with {@link
+ <p>This is the equivalent of launching an activity with {@link
android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT}
- without {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
- Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. -->
+ set and without {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK} set. -->
<enum name="intoExisting" value="1" />
- <!-- A new task rooted at this activity will be created.
- This is the equivalent of with {@link
+ <!-- A new task rooted at this activity will be created. This will happen whether or
+ not there is an existing task whose ComponentName and data URI match
+ that of the launcing intent This is the equivalent of launching an activity
+ with {@link
android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT}
- paired with {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
- Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. -->
+ and {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK} both set. -->
<enum name="always" value="2" />
</attr>
- <!-- Tasks launched by activities with this attribute will remain in the recent task
+ <!-- Tasks launched by activities with this attribute will remain in the recent tasks
list until the last activity in the task is completed. When that happens the task
- will be automatically removed from the recent task list.
+ will be automatically removed from the recent tasks list.
This attribute is the equivalent of {@link
android.content.Intent#FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5375c14..e9d8ccc 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1538,9 +1538,6 @@
-->
<string-array translatable="false" name="config_globalActionsList">
<item>power</item>
- <item>airplane</item>
- <item>bugreport</item>
- <item>silent</item>
<item>users</item>
</string-array>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 52b021f..657f614 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -19,13 +19,9 @@
-->
<resources>
<!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_width">164dp</dimen>
+ <dimen name="thumbnail_width">256dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_height">145dp</dimen>
- <!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="recents_thumbnail_width">256dp</dimen>
- <!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="recents_thumbnail_height">256dp</dimen>
+ <dimen name="thumbnail_height">256dp</dimen>
<!-- The standard size (both width and height) of an application icon that
will be displayed in the app launcher and elsewhere. -->
<dimen name="app_icon_size">48dip</dimen>
@@ -206,9 +202,6 @@
<!-- Default width for a textview error popup -->
<dimen name="textview_error_popup_default_width">240dip</dimen>
- <!-- Volume panel y offset -->
- <dimen name="volume_panel_top">16dp</dimen>
-
<!-- Default padding to apply to AppWidgetHostViews containing widgets targeting API level 14 and up. -->
<dimen name="default_app_widget_padding_left">8dp</dimen>
<dimen name="default_app_widget_padding_top">8dp</dimen>
diff --git a/core/res/res/values/dimens_quantum.xml b/core/res/res/values/dimens_quantum.xml
index 53e97fd..2defee2 100644
--- a/core/res/res/values/dimens_quantum.xml
+++ b/core/res/res/values/dimens_quantum.xml
@@ -47,6 +47,10 @@
<dimen name="text_size_menu_quantum">14sp</dimen>
<dimen name="text_size_button_quantum">14sp</dimen>
+ <dimen name="text_size_large_quantum">22sp</dimen>
+ <dimen name="text_size_medium_quantum">18sp</dimen>
+ <dimen name="text_size_small_quantum">14sp</dimen>
+
<dimen name="floating_window_z">16dp</dimen>
<dimen name="floating_window_margin">32dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ce0d2d5..42ed318 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2178,12 +2178,11 @@
<public type="attr" name="contentInsetLeft" />
<public type="attr" name="contentInsetRight" />
<public type="attr" name="paddingMode" />
+ <public type="attr" name="layout_rowWeight" />
+ <public type="attr" name="layout_columnWeight" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
- <public type="dimen" name="recents_thumbnail_height" />
- <public type="dimen" name="recents_thumbnail_width" />
-
<public-padding type="id" name="l_resource_pad" end="0x01020040" />
<public type="id" name="mask" />
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 4a27ebe..1f4c88d 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -231,14 +231,6 @@
<item name="windowExitAnimation">@anim/fast_fade_out</item>
</style>
- <!-- Window animations for swipe-dismissable windows. {@hide} -->
- <style name="Animation.SwipeDismiss">
- <item name="taskOpenEnterAnimation">@anim/swipe_window_enter</item>
- <item name="taskOpenExitAnimation">@anim/swipe_window_exit</item>
- <item name="taskCloseEnterAnimation">@anim/swipe_window_enter</item>
- <item name="taskCloseExitAnimation">@anim/swipe_window_exit</item>
- </style>
-
<!-- Status Bar Styles -->
<style name="TextAppearance.StatusBar">
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
@@ -277,37 +269,6 @@
<item name="android:textColor">#CCCCCC</item>
</style>
- <style name="TextAppearance.StatusBar.Quantum">
- </style>
- <style name="TextAppearance.StatusBar.Quantum.EventContent">
- <item name="android:textColor">#90000000</item>
- <item name="android:textSize">@dimen/notification_text_size</item>
- </style>
- <style name="TextAppearance.StatusBar.Quantum.EventContent.Title">
- <item name="android:textColor">#DD000000</item>
- <item name="android:textSize">@dimen/notification_title_text_size</item>
- </style>
- <style name="TextAppearance.StatusBar.Quantum.EventContent.Line2">
- <item name="android:textSize">@dimen/notification_subtext_size</item>
- </style>
- <style name="TextAppearance.StatusBar.Quantum.EventContent.Info">
- <item name="android:textSize">@dimen/notification_subtext_size</item>
- </style>
- <style name="TextAppearance.StatusBar.Quantum.EventContent.Time">
- <item name="android:textSize">@dimen/notification_subtext_size</item>
- </style>
- <style name="TextAppearance.StatusBar.Quantum.EventContent.Emphasis">
- <item name="android:textColor">#66000000</item>
- </style>
- <style name="Widget.StatusBar.Quantum.ProgressBar"
- parent="Widget.Quantum.Light.ProgressBar.Horizontal">
- <item name="android:progressDrawable">@drawable/notification_quantum_media_progress</item>
- </style>
-
- <style name="Widget.StatusBar.Quantum.ProgressBar"
- parent="Widget.Quantum.Light.ProgressBar.Horizontal">
- </style>
-
<style name="TextAppearance.Small.CalendarViewWeekDayView">
<item name="android:textStyle">bold</item>
</style>
diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml
index 5bac1f9..0c854d3 100644
--- a/core/res/res/values/styles_micro.xml
+++ b/core/res/res/values/styles_micro.xml
@@ -14,6 +14,19 @@
limitations under the License.
-->
<resources>
+ <style name="Animation.Micro"/>
+
+ <style name="Animation.Micro.Activity" parent="Animation.Holo.Activity">
+ <item name="activityOpenEnterAnimation">@anim/slide_in_micro</item>
+ <item name="activityOpenExitAnimation">@null</item>
+ <item name="activityCloseEnterAnimation">@null</item>
+ <item name="activityCloseExitAnimation">@anim/slide_out_micro</item>
+ <item name="taskOpenEnterAnimation">@anim/slide_in_micro</item>
+ <item name="taskOpenExitAnimation">@null</item>
+ <item name="taskCloseEnterAnimation">@null</item>
+ <item name="taskCloseExitAnimation">@anim/slide_out_micro</item>
+ </style>
+
<style name="AlertDialog.Micro" parent="AlertDialog.Holo.Light">
<item name="fullDark">@null</item>
<item name="topDark">@null</item>
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index e528278..2e7a5b1 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -182,7 +182,10 @@
<item name="textColorLink">?attr/textColorLinkInverse</item>
</style>
- <style name="TextAppearance.Quantum.Large" parent="TextAppearance.Quantum.Headline" />
+ <style name="TextAppearance.Quantum.Large">
+ <item name="textSize">@dimen/text_size_large_quantum</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ </style>
<style name="TextAppearance.Quantum.Large.Inverse">
<item name="textColor">?attr/textColorPrimaryInverse</item>
@@ -191,7 +194,10 @@
<item name="textColorLink">?attr/textColorLinkInverse</item>
</style>
- <style name="TextAppearance.Quantum.Medium" parent="TextAppearance.Quantum.Body1" />
+ <style name="TextAppearance.Quantum.Medium">
+ <item name="textSize">@dimen/text_size_medium_quantum</item>
+ <item name="textColor">?attr/textColorSecondary</item>
+ </style>
<style name="TextAppearance.Quantum.Medium.Inverse">
<item name="textColor">?attr/textColorSecondaryInverse</item>
@@ -200,7 +206,10 @@
<item name="textColorLink">?attr/textColorLinkInverse</item>
</style>
- <style name="TextAppearance.Quantum.Small" parent="TextAppearance.Quantum.Caption" />
+ <style name="TextAppearance.Quantum.Small">
+ <item name="textSize">@dimen/text_size_small_quantum</item>
+ <item name="textColor">?attr/textColorTertiary</item>
+ </style>
<style name="TextAppearance.Quantum.Small.Inverse">
<item name="textColor">?attr/textColorTertiaryInverse</item>
@@ -351,6 +360,38 @@
<item name="textStyle">bold</item>
</style>
+ <style name="TextAppearance.StatusBar.Quantum" />
+
+ <style name="TextAppearance.StatusBar.Quantum.EventContent">
+ <item name="android:textColor">#90000000</item>
+ <item name="android:textSize">@dimen/notification_text_size</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Quantum.EventContent.Title">
+ <item name="android:textColor">#DD000000</item>
+ <item name="android:textSize">@dimen/notification_title_text_size</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Quantum.EventContent.Line2">
+ <item name="android:textSize">@dimen/notification_subtext_size</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Quantum.EventContent.Info">
+ <item name="android:textSize">@dimen/notification_subtext_size</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Quantum.EventContent.Time">
+ <item name="android:textSize">@dimen/notification_subtext_size</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Quantum.EventContent.Emphasis">
+ <item name="android:textColor">#66000000</item>
+ </style>
+
+ <style name="Widget.StatusBar.Quantum.ProgressBar" parent="Widget.Quantum.Light.ProgressBar.Horizontal">
+ <item name="android:progressDrawable">@drawable/notification_quantum_media_progress</item>
+ </style>
+
<!-- Widget Styles -->
<style name="Quantum"/>
@@ -390,7 +431,7 @@
</style>
<style name="Widget.Quantum.Button.Toggle">
- <item name="background">@drawable/btn_toggle_holo_dark</item>
+ <item name="background">@drawable/btn_toggle_quantum</item>
<item name="textOn">@string/capital_on</item>
<item name="textOff">@string/capital_off</item>
<item name="textAppearance">?attr/textAppearanceSmall</item>
@@ -459,13 +500,13 @@
<style name="Widget.Quantum.CompoundButton.Switch">
<item name="track">@drawable/switch_track_quantum</item>
- <item name="thumb">@drawable/switch_inner_quantum</item>
+ <item name="thumb">@drawable/switch_thumb_quantum_anim</item>
<item name="splitTrack">true</item>
<item name="switchTextAppearance">@style/TextAppearance.Quantum.Widget.Switch</item>
<item name="textOn"></item>
<item name="textOff"></item>
- <item name="switchMinWidth">72dip</item>
- <item name="switchPadding">16dip</item>
+ <item name="switchMinWidth">4dip</item>
+ <item name="switchPadding">4dip</item>
<item name="background">?attr/selectableItemBackground</item>
</style>
@@ -579,7 +620,7 @@
<item name="indeterminateOnly">false</item>
<item name="progressDrawable">@drawable/scrubber_progress_horizontal_quantum</item>
<item name="indeterminateDrawable">@drawable/scrubber_progress_horizontal_quantum</item>
- <item name="thumb">@drawable/scrubber_control_selector_quantum</item>
+ <item name="thumb">@drawable/scrubber_control_quantum_anim</item>
<item name="splitTrack">true</item>
<item name="focusable">true</item>
<item name="paddingStart">16dip</item>
@@ -784,15 +825,7 @@
<style name="Widget.Quantum.Light.Button.Borderless" parent="Widget.Quantum.Button.Borderless"/>
<style name="Widget.Quantum.Light.Button.Borderless.Small" parent="Widget.Quantum.Button.Borderless.Small"/>
<style name="Widget.Quantum.Light.Button.Inset" parent="Widget.Quantum.Button.Inset"/>
-
- <style name="Widget.Quantum.Light.Button.Toggle">
- <item name="background">@drawable/btn_toggle_holo_light</item>
- <item name="textOn">@string/capital_on</item>
- <item name="textOff">@string/capital_off</item>
- <item name="textAppearance">?attr/textAppearanceSmall</item>
- <item name="minHeight">48dip</item>
- </style>
-
+ <style name="Widget.Quantum.Light.Button.Toggle" parent="Widget.Quantum.Button.Toggle" />
<style name="Widget.Quantum.Light.ButtonBar" parent="Widget.Quantum.ButtonBar"/>
<style name="Widget.Quantum.Light.ButtonBar.AlertDialog" parent="Widget.Quantum.ButtonBar.AlertDialog"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dcff978..69f73e5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -69,8 +69,6 @@
<java-symbol type="id" name="edittext_container" />
<java-symbol type="id" name="enter_pin_section" />
<java-symbol type="id" name="expand_activities_button" />
- <java-symbol type="id" name="expand_button" />
- <java-symbol type="id" name="expand_button_divider" />
<java-symbol type="id" name="expires_on" />
<java-symbol type="id" name="find_next" />
<java-symbol type="id" name="find_prev" />
@@ -161,9 +159,7 @@
<java-symbol type="id" name="share" />
<java-symbol type="id" name="shortcut" />
<java-symbol type="id" name="skip_button" />
- <java-symbol type="id" name="slider_group" />
<java-symbol type="id" name="split_action_bar" />
- <java-symbol type="id" name="stream_icon" />
<java-symbol type="id" name="submit_area" />
<java-symbol type="id" name="switch_new" />
<java-symbol type="id" name="switch_old" />
@@ -181,7 +177,6 @@
<java-symbol type="id" name="topPanel" />
<java-symbol type="id" name="up" />
<java-symbol type="id" name="value" />
- <java-symbol type="id" name="visible_panel" />
<java-symbol type="id" name="websearch" />
<java-symbol type="id" name="wifi_p2p_wps_pin" />
<java-symbol type="id" name="year" />
@@ -343,7 +338,6 @@
<java-symbol type="dimen" name="search_view_preferred_width" />
<java-symbol type="dimen" name="textview_error_popup_default_width" />
<java-symbol type="dimen" name="toast_y_offset" />
- <java-symbol type="dimen" name="volume_panel_top" />
<java-symbol type="dimen" name="action_bar_stacked_max_height" />
<java-symbol type="dimen" name="action_bar_stacked_tab_max_width" />
<java-symbol type="dimen" name="notification_text_size" />
@@ -1199,8 +1193,6 @@
<java-symbol type="layout" name="time_picker_legacy" />
<java-symbol type="layout" name="time_picker_dialog" />
<java-symbol type="layout" name="transient_notification" />
- <java-symbol type="layout" name="volume_adjust" />
- <java-symbol type="layout" name="volume_adjust_item" />
<java-symbol type="layout" name="voice_interaction_session" />
<java-symbol type="layout" name="web_text_view_dropdown" />
<java-symbol type="layout" name="webview_find" />
diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml
index ebdab5b..7e0467b 100644
--- a/core/res/res/values/themes_micro.xml
+++ b/core/res/res/values/themes_micro.xml
@@ -19,15 +19,14 @@
<item name="alertDialogStyle">@style/AlertDialog.Micro</item>
<item name="dialogTheme">@style/Theme.Micro.Dialog</item>
<item name="textViewStyle">@style/Widget.Micro.TextView</item>
-
<item name="numberPickerStyle">@style/Widget.Micro.NumberPicker</item>
- <item name="windowAnimationStyle">@style/Animation.SwipeDismiss</item>
+ <item name="windowAnimationStyle">@style/Animation.Micro.Activity</item>
<item name="windowBackground">@color/black</item>
<item name="windowContentOverlay">@null</item>
<item name="windowIsFloating">false</item>
<item name="windowIsTranslucent">true</item>
<item name="windowSwipeToDismiss">true</item>
- </style>
+ </style>
<style name="Theme.Micro.Light" parent="Theme.Holo.Light.NoActionBar">
<item name="alertDialogTheme">@style/Theme.Micro.Dialog.Alert</item>
@@ -35,7 +34,7 @@
<item name="dialogTheme">@style/Theme.Micro.Dialog</item>
<item name="textViewStyle">@style/Widget.Micro.TextView</item>
<item name="numberPickerStyle">@style/Widget.Micro.NumberPicker</item>
- <item name="windowAnimationStyle">@style/Animation.SwipeDismiss</item>
+ <item name="windowAnimationStyle">@style/Animation.Micro.Activity</item>
<item name="windowBackground">@color/white</item>
<item name="windowContentOverlay">@null</item>
<item name="windowIsFloating">false</item>
diff --git a/core/tests/coretests/src/android/os/FileBridgeTest.java b/core/tests/coretests/src/android/os/FileBridgeTest.java
new file mode 100644
index 0000000..d4f6b1f
--- /dev/null
+++ b/core/tests/coretests/src/android/os/FileBridgeTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.FileBridge.FileBridgeOutputStream;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+
+import libcore.io.Streams;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Random;
+
+public class FileBridgeTest extends AndroidTestCase {
+
+ private File file;
+ private FileOutputStream fileOs;
+ private FileBridge bridge;
+ private FileBridgeOutputStream client;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ file = getContext().getFileStreamPath("meow.dat");
+ file.delete();
+
+ fileOs = new FileOutputStream(file);
+
+ bridge = new FileBridge();
+ bridge.setTargetFile(fileOs.getFD());
+ bridge.start();
+ client = new FileBridgeOutputStream(bridge.getClientSocket());
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ fileOs.close();
+ file.delete();
+ }
+
+ private void assertOpen() throws Exception {
+ assertFalse("expected open", bridge.isClosed());
+ }
+
+ private void closeAndAssertClosed() throws Exception {
+ client.close();
+
+ // Wait a beat for things to settle down
+ SystemClock.sleep(200);
+ assertTrue("expected closed", bridge.isClosed());
+ }
+
+ private void assertContents(byte[] expected) throws Exception {
+ MoreAsserts.assertEquals(expected, Streams.readFully(new FileInputStream(file)));
+ }
+
+ public void testNoWriteNoSync() throws Exception {
+ assertOpen();
+ closeAndAssertClosed();
+ }
+
+ public void testNoWriteSync() throws Exception {
+ assertOpen();
+ client.flush();
+ closeAndAssertClosed();
+ }
+
+ public void testWriteNoSync() throws Exception {
+ assertOpen();
+ client.write("meow".getBytes(StandardCharsets.UTF_8));
+ closeAndAssertClosed();
+ assertContents("meow".getBytes(StandardCharsets.UTF_8));
+ }
+
+ public void testWriteSync() throws Exception {
+ assertOpen();
+ client.write("cake".getBytes(StandardCharsets.UTF_8));
+ client.flush();
+ closeAndAssertClosed();
+ assertContents("cake".getBytes(StandardCharsets.UTF_8));
+ }
+
+ public void testWriteSyncWrite() throws Exception {
+ assertOpen();
+ client.write("meow".getBytes(StandardCharsets.UTF_8));
+ client.flush();
+ client.write("cake".getBytes(StandardCharsets.UTF_8));
+ closeAndAssertClosed();
+ assertContents("meowcake".getBytes(StandardCharsets.UTF_8));
+ }
+
+ public void testEmptyWrite() throws Exception {
+ assertOpen();
+ client.write(new byte[0]);
+ closeAndAssertClosed();
+ assertContents(new byte[0]);
+ }
+
+ public void testWriteAfterClose() throws Exception {
+ assertOpen();
+ client.write("meow".getBytes(StandardCharsets.UTF_8));
+ closeAndAssertClosed();
+ try {
+ client.write("cake".getBytes(StandardCharsets.UTF_8));
+ fail("wrote after close!");
+ } catch (IOException expected) {
+ }
+ assertContents("meow".getBytes(StandardCharsets.UTF_8));
+ }
+
+ public void testRandomWrite() throws Exception {
+ final Random r = new Random();
+ final ByteArrayOutputStream result = new ByteArrayOutputStream();
+
+ for (int i = 0; i < 512; i++) {
+ final byte[] test = new byte[r.nextInt(24169)];
+ r.nextBytes(test);
+ result.write(test);
+ client.write(test);
+ client.flush();
+ }
+
+ closeAndAssertClosed();
+ assertContents(result.toByteArray());
+ }
+
+ public void testGiantWrite() throws Exception {
+ final byte[] test = new byte[263401];
+ new Random().nextBytes(test);
+
+ assertOpen();
+ client.write(test);
+ closeAndAssertClosed();
+ assertContents(test);
+ }
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
new file mode 100644
index 0000000..d649154
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
@@ -0,0 +1,42 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+
+## The application with a minimal main dex
+include $(CLEAR_VARS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := MultiDexLegacyAndException
+
+mainDexList:= \
+ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
+
+LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
+
+include $(BUILD_PACKAGE)
+
+$(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
+ $(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
+ echo "com/android/multidexlegacyandexception/Test.class" >> $@
+
+$(built_dex_intermediate): $(mainDexList)
+
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml
new file mode 100644
index 0000000..7fff711
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.multidexlegacyandexception"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18"/>
+
+ <application
+ android:name="com.android.multidexlegacyandexception.TestApplication"
+ android:label="multidexlegacyandexception"
+ >
+ <activity
+ android:name="com.android.multidexlegacyandexception.MainActivity"
+ android:label="multidexlegacyandexception" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.multidexlegacyandexception"
+ android:label="Test for MultiDexLegacyAndException" />
+</manifest>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/layout/activity_main.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/layout/activity_main.xml
new file mode 100644
index 0000000..37eb613
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/layout/activity_main.xml
@@ -0,0 +1,13 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ tools:context=".MainActivity" >
+
+ <TextView
+ android:id="@+id/label_nb"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/label_nb" />
+
+</RelativeLayout>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/values/strings.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/values/strings.xml
new file mode 100644
index 0000000..e56e049
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/values/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">MultidexLegacyAndException</string>
+ <string name="action_settings">Settings</string>
+ <string name="label_nb">Here\'s the count: </string>
+
+</resources>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.java
new file mode 100644
index 0000000..d6883ec
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multidexlegacyandexception;
+
+public class CaughtOnlyByIntermediateException extends RuntimeException {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyException.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyException.java
new file mode 100644
index 0000000..4903e01
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyException.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multidexlegacyandexception;
+
+public class CaughtOnlyException extends RuntimeException {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ClassInSecondaryDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ClassInSecondaryDex.java
new file mode 100644
index 0000000..b08a11a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ClassInSecondaryDex.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multidexlegacyandexception;
+
+public class ClassInSecondaryDex {
+ private boolean condition;
+
+ public ClassInSecondaryDex(boolean condition) {
+ this.condition = condition;
+ }
+
+ public void canThrow1() throws ExceptionInMainDex, ExceptionInMainDex2,
+ ExceptionInSecondaryDexWithSuperInMain {
+ if (condition) {
+ throw new ExceptionInMainDex();
+ }
+ }
+
+ public void canThrow2() throws ExceptionInSecondaryDex, ExceptionInSecondaryDex2,
+ ExceptionInSecondaryDexWithSuperInMain {
+ if (condition) {
+ throw new ExceptionInSecondaryDex();
+ }
+ }
+
+ public static void canThrowAll(Throwable toThrow) throws Throwable {
+ if (toThrow != null) {
+ throw toThrow;
+ }
+ }
+
+ public int get1() {
+ try {
+ canThrow1();
+ canThrow2();
+ return 1;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (ExceptionInSecondaryDex e) {
+ return 11;
+ } catch (OutOfMemoryError e) {
+ return 12;
+ } catch (CaughtOnlyException e) {
+ return 17;
+ } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex e) {
+ return 23;
+ }
+ }
+
+ public int get2() {
+ try {
+ canThrow2();
+ canThrow1();
+ return 1;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (ExceptionInSecondaryDex e) {
+ return 11;
+ } catch (OutOfMemoryError e) {
+ return 12;
+ } catch (CaughtOnlyException e) {
+ return 17;
+ } catch (SuperExceptionInSecondaryDex e) {
+ return 23;
+ } catch (SuperExceptionInMainDex e) {
+ return 27;
+ }
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex.java
new file mode 100644
index 0000000..7fc3d73
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multidexlegacyandexception;
+
+public class ExceptionInMainDex extends SuperExceptionInMainDex {
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex2.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex2.java
new file mode 100644
index 0000000..3fbeac6
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex2.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multidexlegacyandexception;
+
+public class ExceptionInMainDex2 extends SuperExceptionInMainDex {
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex.java
new file mode 100644
index 0000000..9401c05
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multidexlegacyandexception;
+
+public class ExceptionInSecondaryDex extends SuperExceptionInSecondaryDex {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex2.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex2.java
new file mode 100644
index 0000000..d1aa103
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex2.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multidexlegacyandexception;
+
+public class ExceptionInSecondaryDex2 extends SuperExceptionInSecondaryDex {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDexWithSuperInMain.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDexWithSuperInMain.java
new file mode 100644
index 0000000..9327882
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDexWithSuperInMain.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multidexlegacyandexception;
+
+public class ExceptionInSecondaryDexWithSuperInMain extends SuperExceptionInMainDex {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/IntermediateClass.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/IntermediateClass.java
new file mode 100644
index 0000000..dfdc4af
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/IntermediateClass.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.multidexlegacyandexception;
+
+public class IntermediateClass {
+
+ public static int get1(boolean condition) {
+ return new ClassInSecondaryDex(condition).get1();
+ }
+
+ public static int get2(boolean condition) {
+ return new ClassInSecondaryDex(condition).get2();
+ }
+
+ public static int get3(boolean condition) {
+ ClassInSecondaryDex thrower = new ClassInSecondaryDex(condition);
+ try {
+ thrower.canThrow2();
+ thrower.canThrow1();
+ return 1;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (ExceptionInSecondaryDex e) {
+ return 11;
+ } catch (ExceptionInMainDex2 e) {
+ return 10;
+ } catch (ExceptionInSecondaryDex2 e) {
+ return 11;
+ } catch (OutOfMemoryError e) {
+ return 12;
+ } catch (CaughtOnlyException e) {
+ return 17;
+ } catch (ExceptionInSecondaryDexWithSuperInMain e) {
+ return 39;
+ } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex|CaughtOnlyByIntermediateException e) {
+ return 23;
+ }
+ }
+
+ public static int get4(boolean condition) {
+ ClassInSecondaryDex thrower = new ClassInSecondaryDex(condition);
+ try {
+ thrower.canThrow2();
+ thrower.canThrow1();
+ return 1;
+ } catch (ExceptionInSecondaryDexWithSuperInMain e) {
+ return 39;
+ } catch (ExceptionInSecondaryDex e) {
+ return 11;
+ } catch (ExceptionInSecondaryDex2 e) {
+ return 11;
+ } catch (OutOfMemoryError e) {
+ return 12;
+ } catch (ExceptionInMainDex2 e) {
+ return 10;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (CaughtOnlyException e) {
+ return 17;
+ } catch (SuperExceptionInSecondaryDex e) {
+ } catch (SuperExceptionInMainDex e) {
+ } catch (CaughtOnlyByIntermediateException e) {
+ return 35;
+ }
+ return 39;
+ }
+
+
+ public static int get5(Throwable thrown) {
+ try {
+ ClassInSecondaryDex.canThrowAll(thrown);
+ return 1;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (ExceptionInSecondaryDex e) {
+ return 11;
+ } catch (ExceptionInMainDex2 e) {
+ return 12;
+ } catch (ExceptionInSecondaryDex2 e) {
+ return 13;
+ } catch (OutOfMemoryError e) {
+ return 14;
+ } catch (CaughtOnlyException e) {
+ return 17;
+ } catch (ExceptionInSecondaryDexWithSuperInMain e) {
+ return 39;
+ } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex|CaughtOnlyByIntermediateException e) {
+ return 23;
+ } catch (Throwable e) {
+ return 37;
+ }
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MainActivity.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MainActivity.java
new file mode 100644
index 0000000..dd2ce7a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MainActivity.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.multidexlegacyandexception;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class MainActivity extends Activity {
+
+ public MainActivity() {
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ }
+
+ public int get1(boolean condition) {
+ return IntermediateClass.get1(condition);
+ }
+
+ public int get2(boolean condition) {
+ return IntermediateClass.get2(condition);
+ }
+ public int get3(boolean condition) {
+ return MiniIntermediateClass.get3(condition);
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MiniIntermediateClass.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MiniIntermediateClass.java
new file mode 100644
index 0000000..5957662
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MiniIntermediateClass.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.multidexlegacyandexception;
+
+public class MiniIntermediateClass {
+
+ public static int get3(boolean condition) {
+ ClassInSecondaryDex thrower = new ClassInSecondaryDex(condition);
+ try {
+ thrower.canThrow2();
+ thrower.canThrow1();
+ return 1;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (ExceptionInSecondaryDex e) {
+ return 11;
+ } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex e) {
+ return 23;
+ }
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInMainDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInMainDex.java
new file mode 100644
index 0000000..c94b30a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInMainDex.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multidexlegacyandexception;
+
+public class SuperExceptionInMainDex extends Exception {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInSecondaryDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInSecondaryDex.java
new file mode 100644
index 0000000..6366fae
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInSecondaryDex.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multidexlegacyandexception;
+
+public class SuperExceptionInSecondaryDex extends Exception {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/Test.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/Test.java
new file mode 100644
index 0000000..5e931bc
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/Test.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.multidexlegacyandexception;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+ * Run the tests with: <code>adb shell am instrument -w
+ com.android.multidexlegacyandexception/android.test.InstrumentationTestRunner
+</code>
+ */
+public class Test extends ActivityInstrumentationTestCase2<MainActivity> {
+ public Test() {
+ super(MainActivity.class);
+ }
+
+ public void testExceptionInMainDex() {
+ assertEquals(10, TestApplication.get(true));
+ }
+
+ public void testExceptionInSecondaryDex() {
+ assertEquals(10, getActivity().get1(true));
+ assertEquals(11, getActivity().get2(true));
+ }
+
+ public void testExceptionInIntermediate() {
+ assertEquals(11, IntermediateClass.get3(true));
+ assertEquals(11, MiniIntermediateClass.get3(true));
+ assertEquals(11, IntermediateClass.get4(true));
+ assertEquals(1, IntermediateClass.get5(null));
+ assertEquals(10, IntermediateClass.get5(new ExceptionInMainDex()));
+ assertEquals(11, IntermediateClass.get5(new ExceptionInSecondaryDex()));
+ assertEquals(12, IntermediateClass.get5(new ExceptionInMainDex2()));
+ assertEquals(13, IntermediateClass.get5(new ExceptionInSecondaryDex2()));
+ assertEquals(14, IntermediateClass.get5(new OutOfMemoryError()));
+ assertEquals(17, IntermediateClass.get5(new CaughtOnlyException()));
+ assertEquals(39, IntermediateClass.get5(new ExceptionInSecondaryDexWithSuperInMain()));
+ assertEquals(23, IntermediateClass.get5(new SuperExceptionInSecondaryDex()));
+ assertEquals(23, IntermediateClass.get5(new SuperExceptionInMainDex()));
+ assertEquals(23, IntermediateClass.get5(new CaughtOnlyByIntermediateException()));
+ assertEquals(37, IntermediateClass.get5(new ArrayIndexOutOfBoundsException()));
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java
new file mode 100644
index 0000000..dece9a4
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.multidexlegacyandexception;
+
+import android.support.multidex.MultiDexApplication;
+
+public class TestApplication extends MultiDexApplication {
+
+ private static void canThrow1(boolean condition) throws ExceptionInMainDex {
+ if (condition) {
+ throw new ExceptionInMainDex();
+ }
+ }
+
+
+ public static int get(boolean condition) {
+ try {
+ canThrow1(condition);
+ return 1;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (OutOfMemoryError e) {
+ return 12;
+ } catch (CaughtOnlyException e) {
+ return 17;
+ } catch (SuperExceptionInMainDex e) {
+ return 27;
+ }
+ }
+
+}
diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
index 0f343b1..c0c20e2 100644
--- a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
@@ -25,8 +25,9 @@
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
-import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
+import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
+import com.android.internal.inputmethod.InputMethodUtils;
import java.util.ArrayList;
import java.util.Arrays;
@@ -39,6 +40,7 @@
private static final boolean DUMMY_FORCE_DEFAULT = false;
private static final int DUMMY_IS_DEFAULT_RES_ID = 0;
private static final String SYSTEM_LOCALE = "en_US";
+ private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
private static InputMethodSubtype createDummySubtype(final String locale) {
final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
@@ -64,142 +66,211 @@
si.exported = true;
si.nonLocalizedLabel = imeLabel;
ri.serviceInfo = si;
- final List<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
- for (String subtypeLocale : subtypeLocales) {
- subtypes.add(createDummySubtype(subtypeLocale));
+ List<InputMethodSubtype> subtypes = null;
+ if (subtypeLocales != null) {
+ subtypes = new ArrayList<InputMethodSubtype>();
+ for (String subtypeLocale : subtypeLocales) {
+ subtypes.add(createDummySubtype(subtypeLocale));
+ }
}
final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod);
- for (int i = 0; i < subtypes.size(); ++i) {
- final String subtypeLocale = subtypeLocales.get(i);
- items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale,
- SYSTEM_LOCALE));
+ if (subtypes == null) {
+ items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi,
+ NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE));
+ } else {
+ for (int i = 0; i < subtypes.size(); ++i) {
+ final String subtypeLocale = subtypeLocales.get(i);
+ items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale,
+ SYSTEM_LOCALE));
+ }
}
}
- private static List<ImeSubtypeListItem> createTestData() {
+ private static List<ImeSubtypeListItem> createEnabledImeSubtypes() {
final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
- addDummyImeSubtypeListItems(items, "switchAwareLatinIme", "switchAwareLatinIme",
- Arrays.asList("en_US", "es_US", "fr"),
+ addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme", Arrays.asList("en_US", "fr"),
true /* supportsSwitchingToNextInputMethod*/);
- addDummyImeSubtypeListItems(items, "nonSwitchAwareLatinIme", "nonSwitchAwareLatinIme",
+ addDummyImeSubtypeListItems(items, "switchUnawareLatinIme", "switchUnawareLatinIme",
Arrays.asList("en_UK", "hi"),
false /* supportsSwitchingToNextInputMethod*/);
- addDummyImeSubtypeListItems(items, "switchAwareJapaneseIme", "switchAwareJapaneseIme",
- Arrays.asList("ja_JP"),
+ addDummyImeSubtypeListItems(items, "subtypeUnawareIme", "subtypeUnawareIme", null,
+ false /* supportsSwitchingToNextInputMethod*/);
+ addDummyImeSubtypeListItems(items, "JapaneseIme", "JapaneseIme", Arrays.asList("ja_JP"),
true /* supportsSwitchingToNextInputMethod*/);
- addDummyImeSubtypeListItems(items, "nonSwitchAwareJapaneseIme", "nonSwitchAwareJapaneseIme",
- Arrays.asList("ja_JP"),
+ addDummyImeSubtypeListItems(items, "switchUnawareJapaneseIme", "switchUnawareJapaneseIme",
+ Arrays.asList("ja_JP"), false /* supportsSwitchingToNextInputMethod*/);
+ return items;
+ }
+
+ private static List<ImeSubtypeListItem> createDisabledImeSubtypes() {
+ final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
+ addDummyImeSubtypeListItems(items,
+ "UnknownIme", "UnknownIme",
+ Arrays.asList("en_US", "hi"),
+ true /* supportsSwitchingToNextInputMethod*/);
+ addDummyImeSubtypeListItems(items,
+ "UnknownSwitchingUnawareIme", "UnknownSwitchingUnawareIme",
+ Arrays.asList("en_US"),
+ false /* supportsSwitchingToNextInputMethod*/);
+ addDummyImeSubtypeListItems(items, "UnknownSubtypeUnawareIme",
+ "UnknownSubtypeUnawareIme", null,
false /* supportsSwitchingToNextInputMethod*/);
return items;
}
- @SmallTest
- public void testGetNextInputMethodImplWithNotOnlyCurrentIme() throws Exception {
- final List<ImeSubtypeListItem> imList = createTestData();
+ private void assertNextInputMethod(final ControllerImpl controller,
+ final boolean onlyCurrentIme,
+ final ImeSubtypeListItem currentItem, final ImeSubtypeListItem nextItem) {
+ InputMethodSubtype subtype = null;
+ if (currentItem.mSubtypeName != null) {
+ subtype = createDummySubtype(currentItem.mSubtypeName.toString());
+ }
+ final ImeSubtypeListItem nextIme = controller.getNextInputMethod(onlyCurrentIme,
+ currentItem.mImi, subtype);
+ assertEquals(nextItem, nextIme);
+ }
- final boolean ONLY_CURRENT_IME = false;
- ImeSubtypeListItem currentIme;
- ImeSubtypeListItem nextIme;
+ private void assertRotationOrder(final ControllerImpl controller,
+ final boolean onlyCurrentIme,
+ final ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList) {
+ final int N = expectedRotationOrderOfImeSubtypeList.length;
+ for (int i = 0; i < N; i++) {
+ final int currentIndex = i;
+ final int nextIndex = (currentIndex + 1) % N;
+ final ImeSubtypeListItem currentItem =
+ expectedRotationOrderOfImeSubtypeList[currentIndex];
+ final ImeSubtypeListItem nextItem = expectedRotationOrderOfImeSubtypeList[nextIndex];
+ assertNextInputMethod(controller, onlyCurrentIme, currentItem, nextItem);
+ }
+ }
- // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
- currentIme = imList.get(0);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(1), nextIme);
- // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr"
- currentIme = imList.get(1);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(2), nextIme);
- // "switchAwareLatinIme/fr" -> "switchAwareJapaneseIme/ja_JP"
- currentIme = imList.get(2);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(5), nextIme);
- // "switchAwareJapaneseIme/ja_JP" -> "switchAwareLatinIme/en_US"
- currentIme = imList.get(5);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(0), nextIme);
-
- // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi"
- currentIme = imList.get(3);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(4), nextIme);
- // "nonSwitchAwareLatinIme/hi" -> "nonSwitchAwareJapaneseIme/ja_JP"
- currentIme = imList.get(4);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(6), nextIme);
- // "nonSwitchAwareJapaneseIme/ja_JP" -> "nonSwitchAwareLatinIme/en_UK"
- currentIme = imList.get(6);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(3), nextIme);
+ private void onUserAction(final ControllerImpl controller,
+ final ImeSubtypeListItem subtypeListItem) {
+ InputMethodSubtype subtype = null;
+ if (subtypeListItem.mSubtypeName != null) {
+ subtype = createDummySubtype(subtypeListItem.mSubtypeName.toString());
+ }
+ controller.onUserActionLocked(subtypeListItem.mImi, subtype);
}
@SmallTest
- public void testGetNextInputMethodImplWithOnlyCurrentIme() throws Exception {
- final List<ImeSubtypeListItem> imList = createTestData();
+ public void testControllerImpl() throws Exception {
+ final List<ImeSubtypeListItem> disabledItems = createDisabledImeSubtypes();
+ final ImeSubtypeListItem disabledIme_en_US = disabledItems.get(0);
+ final ImeSubtypeListItem disabledIme_hi = disabledItems.get(1);
+ final ImeSubtypeListItem disabledSwitchingUnawareIme = disabledItems.get(2);
+ final ImeSubtypeListItem disabledSubtypeUnawareIme = disabledItems.get(3);
- final boolean ONLY_CURRENT_IME = true;
- ImeSubtypeListItem currentIme;
- ImeSubtypeListItem nextIme;
+ final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
+ final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
+ final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
+ final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2);
+ final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3);
+ final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
+ final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
+ final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
- // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
- currentIme = imList.get(0);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(1), nextIme);
- // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr"
- currentIme = imList.get(1);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(2), nextIme);
- // "switchAwareLatinIme/fr" -> "switchAwareLatinIme/en_US"
- currentIme = imList.get(2);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(0), nextIme);
+ final ControllerImpl controller = new ControllerImpl(enabledItems);
- // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi"
- currentIme = imList.get(3);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(4), nextIme);
- // "nonSwitchAwareLatinIme/hi" -> "switchAwareLatinIme/en_UK"
- currentIme = imList.get(4);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(3), nextIme);
+ // switching-aware loop
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ latinIme_en_US, latinIme_fr, japaneseIme_ja_JP);
- // "switchAwareJapaneseIme/ja_JP" -> null
- currentIme = imList.get(5);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertNull(nextIme);
+ // switching-unaware loop
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_JP);
- // "nonSwitchAwareJapaneseIme/ja_JP" -> null
- currentIme = imList.get(6);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertNull(nextIme);
+ // test onlyCurrentIme == true
+ assertRotationOrder(controller, true /* onlyCurrentIme */,
+ latinIme_en_US, latinIme_fr);
+ assertRotationOrder(controller, true /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ subtypeUnawareIme, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ japaneseIme_ja_JP, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ switchUnawareJapaneseIme_ja_JP, null);
+
+ // Make sure that disabled IMEs are not accepted.
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ disabledIme_en_US, null);
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ disabledIme_hi, null);
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ disabledSwitchingUnawareIme, null);
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ disabledSubtypeUnawareIme, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ disabledIme_en_US, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ disabledIme_hi, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ disabledSwitchingUnawareIme, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ disabledSubtypeUnawareIme, null);
}
- }
+
+ // This test is disabled until DynamicRotationList is enabled.
+ @SmallTest
+ public void DISABLED_testControllerImplWithUserAction() throws Exception {
+ final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
+ final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
+ final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
+ final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2);
+ final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3);
+ final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
+ final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
+ final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
+
+ final ControllerImpl controller = new ControllerImpl(enabledItems);
+
+ // === switching-aware loop ===
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ latinIme_en_US, latinIme_fr, japaneseIme_ja_JP);
+ // Then notify that a user did something for latinIme_fr.
+ onUserAction(controller, latinIme_fr);
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ latinIme_fr, latinIme_en_US, japaneseIme_ja_JP);
+ // Then notify that a user did something for latinIme_fr again.
+ onUserAction(controller, latinIme_fr);
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ latinIme_fr, latinIme_en_US, japaneseIme_ja_JP);
+ // Then notify that a user did something for japaneseIme_ja_JP.
+ onUserAction(controller, latinIme_fr);
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ japaneseIme_ja_JP, latinIme_fr, latinIme_en_US);
+ // Check onlyCurrentIme == true.
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ japaneseIme_ja_JP, null);
+ assertRotationOrder(controller, true /* onlyCurrentIme */,
+ latinIme_fr, latinIme_en_US);
+ assertRotationOrder(controller, true /* onlyCurrentIme */,
+ latinIme_en_US, latinIme_fr);
+
+ // === switching-unaware loop ===
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_JP);
+ // User action should be ignored for switching unaware IMEs.
+ onUserAction(controller, switchingUnawarelatinIme_hi);
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_JP);
+ // User action should be ignored for switching unaware IMEs.
+ onUserAction(controller, switchUnawareJapaneseIme_ja_JP);
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_JP);
+ // Check onlyCurrentIme == true.
+ assertRotationOrder(controller, true /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ subtypeUnawareIme, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ switchUnawareJapaneseIme_ja_JP, null);
+ }
+}
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 452c575..c6bccfe 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -105,8 +105,11 @@
ifeq ($(MINIMAL_FONT_FOOTPRINT),true)
+$(eval $(call create-font-symlink,Roboto-Black.ttf,Roboto-Bold.ttf))
$(eval $(call create-font-symlink,Roboto-Light.ttf,Roboto-Regular.ttf))
$(eval $(call create-font-symlink,Roboto-LightItalic.ttf,Roboto-Italic.ttf))
+$(eval $(call create-font-symlink,Roboto-Medium.ttf,Roboto-Regular.ttf))
+$(eval $(call create-font-symlink,Roboto-MediumItalic.ttf,Roboto-Italic.ttf))
$(eval $(call create-font-symlink,Roboto-Thin.ttf,Roboto-Regular.ttf))
$(eval $(call create-font-symlink,Roboto-ThinItalic.ttf,Roboto-Italic.ttf))
$(eval $(call create-font-symlink,RobotoCondensed-Regular.ttf,Roboto-Regular.ttf))
@@ -116,8 +119,11 @@
else # !MINIMAL_FONT
font_src_files += \
+ Roboto-Black.ttf \
Roboto-Light.ttf \
Roboto-LightItalic.ttf \
+ Roboto-Medium.ttf \
+ Roboto-MediumItalic.ttf \
Roboto-Thin.ttf \
Roboto-ThinItalic.ttf \
RobotoCondensed-Regular.ttf \
diff --git a/data/fonts/Roboto-Black.ttf b/data/fonts/Roboto-Black.ttf
new file mode 100644
index 0000000..2cdbe43
--- /dev/null
+++ b/data/fonts/Roboto-Black.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Bold.ttf b/data/fonts/Roboto-Bold.ttf
index c5b9c67..15c9b4e 100644
--- a/data/fonts/Roboto-Bold.ttf
+++ b/data/fonts/Roboto-Bold.ttf
Binary files differ
diff --git a/data/fonts/Roboto-BoldItalic.ttf b/data/fonts/Roboto-BoldItalic.ttf
index 0320214..a0abf30 100644
--- a/data/fonts/Roboto-BoldItalic.ttf
+++ b/data/fonts/Roboto-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Italic.ttf b/data/fonts/Roboto-Italic.ttf
index 38ba570..67b5394 100644
--- a/data/fonts/Roboto-Italic.ttf
+++ b/data/fonts/Roboto-Italic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Light.ttf b/data/fonts/Roboto-Light.ttf
index 271606b..d9fb64a 100644
--- a/data/fonts/Roboto-Light.ttf
+++ b/data/fonts/Roboto-Light.ttf
Binary files differ
diff --git a/data/fonts/Roboto-LightItalic.ttf b/data/fonts/Roboto-LightItalic.ttf
index 17ef355..1fd1d31 100644
--- a/data/fonts/Roboto-LightItalic.ttf
+++ b/data/fonts/Roboto-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Medium.ttf b/data/fonts/Roboto-Medium.ttf
new file mode 100644
index 0000000..c63c115
--- /dev/null
+++ b/data/fonts/Roboto-Medium.ttf
Binary files differ
diff --git a/data/fonts/Roboto-MediumItalic.ttf b/data/fonts/Roboto-MediumItalic.ttf
new file mode 100644
index 0000000..cd7c835
--- /dev/null
+++ b/data/fonts/Roboto-MediumItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Regular.ttf b/data/fonts/Roboto-Regular.ttf
index 7469063..9cb4a5a 100644
--- a/data/fonts/Roboto-Regular.ttf
+++ b/data/fonts/Roboto-Regular.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Thin.ttf b/data/fonts/Roboto-Thin.ttf
index 74efe4d..f02f100 100644
--- a/data/fonts/Roboto-Thin.ttf
+++ b/data/fonts/Roboto-Thin.ttf
Binary files differ
diff --git a/data/fonts/Roboto-ThinItalic.ttf b/data/fonts/Roboto-ThinItalic.ttf
index f08ea51..12a2ce0 100644
--- a/data/fonts/Roboto-ThinItalic.ttf
+++ b/data/fonts/Roboto-ThinItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Bold.ttf b/data/fonts/RobotoCondensed-Bold.ttf
index 1252d00..1079af6 100644
--- a/data/fonts/RobotoCondensed-Bold.ttf
+++ b/data/fonts/RobotoCondensed-Bold.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-BoldItalic.ttf b/data/fonts/RobotoCondensed-BoldItalic.ttf
index e914a07..e7f13c2 100644
--- a/data/fonts/RobotoCondensed-BoldItalic.ttf
+++ b/data/fonts/RobotoCondensed-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Italic.ttf b/data/fonts/RobotoCondensed-Italic.ttf
index 8a570cf..7fa04481 100644
--- a/data/fonts/RobotoCondensed-Italic.ttf
+++ b/data/fonts/RobotoCondensed-Italic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Light.ttf b/data/fonts/RobotoCondensed-Light.ttf
index 41d212a..96b75dd 100644
--- a/data/fonts/RobotoCondensed-Light.ttf
+++ b/data/fonts/RobotoCondensed-Light.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-LightItalic.ttf b/data/fonts/RobotoCondensed-LightItalic.ttf
old mode 100755
new mode 100644
index dd54971..7a2c164
--- a/data/fonts/RobotoCondensed-LightItalic.ttf
+++ b/data/fonts/RobotoCondensed-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Regular.ttf b/data/fonts/RobotoCondensed-Regular.ttf
index a16b9cb..734cc40 100644
--- a/data/fonts/RobotoCondensed-Regular.ttf
+++ b/data/fonts/RobotoCondensed-Regular.ttf
Binary files differ
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 2312a04..e5573bb 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -24,8 +24,11 @@
Roboto-Bold.ttf \
Roboto-Italic.ttf \
Roboto-BoldItalic.ttf \
+ Roboto-Black.ttf \
Roboto-Light.ttf \
Roboto-LightItalic.ttf \
+ Roboto-Medium.ttf \
+ Roboto-MediumItalic.ttf \
Roboto-Thin.ttf \
Roboto-ThinItalic.ttf \
RobotoCondensed-Regular.ttf \
diff --git a/data/fonts/system_fonts.xml b/data/fonts/system_fonts.xml
index 97b7fd8..646b33b 100644
--- a/data/fonts/system_fonts.xml
+++ b/data/fonts/system_fonts.xml
@@ -68,6 +68,25 @@
<family>
<nameset>
+ <name>sans-serif-medium</name>
+ </nameset>
+ <fileset>
+ <file>Roboto-Medium.ttf</file>
+ <file>Roboto-MediumItalic.ttf</file>
+ </fileset>
+ </family>
+
+ <family>
+ <nameset>
+ <name>sans-serif-black</name>
+ </nameset>
+ <fileset>
+ <file>Roboto-Black.ttf</file>
+ </fileset>
+ </family>
+
+ <family>
+ <nameset>
<name>sans-serif-condensed-light</name>
</nameset>
<fileset>
diff --git a/docs/html/community/index.html b/docs/html/community/index.html
index eeb1c51..e3834ba 100644
--- a/docs/html/community/index.html
+++ b/docs/html/community/index.html
@@ -34,6 +34,9 @@
</script>
<style>
+#header {
+ padding: 2.2em 0 0.2em 0;
+}
#header-wrap h1 {
margin:0;
padding:0;
diff --git a/docs/html/distribute/tools/promote/brand.jd b/docs/html/distribute/tools/promote/brand.jd
index 2116c0f1..9b9f9a3 100644
--- a/docs/html/distribute/tools/promote/brand.jd
+++ b/docs/html/distribute/tools/promote/brand.jd
@@ -9,6 +9,11 @@
promotional materials. You can use the icons and other assets on this page
provided that you follow the guidelines.</p>
+<p>Use of the Android or Google Play brands must be reviewed by the Android
+Partner Marketing team. Use the <a
+href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android and Google Play Brand Permissions Inquiry form</a> to submit your
+marketing for review.</p>
+
<h2 id="brand-android">Android</h2>
<p>The following are guidelines for the Android brand
@@ -29,10 +34,12 @@
<li><span style="color:red">Incorrect</span>: "Android MediaPlayer"</li>
<li><span style="color:green">Correct</span>: "MediaPlayer for Android"</li>
</ul>
- <p>If used with your logo, "for Android" needs to be smaller in size than your logo.
+ <p>If used with your logo, "for Android" should be no larger than 90% of your logo’s size.
First instance of this use should be followed by a TM symbol, "for Android™".</p>
</li>
- <li>Android may be used as a descriptor, as long as it is followed by a proper generic term.
+ <li>Android may be used as a descriptor, as long as it is followed by a
+ proper generic term. (Think of "Android" as a term used in place of
+ "the Android platform.")
<ul>
<li><span style="color:red">Incorrect</span>: "Android MediaPlayer" or "Android XYZ app"</li>
<li><span style="color:green">Correct</span>: "Android features" or "Android applications"</li>
@@ -62,14 +69,14 @@
<p>When using the Android Robot or any modification of it, proper attribution is
required under the terms of the <a href="http://creativecommons.org/licenses/by/3.0/">Creative
-Commons Attribution</a> license:</p>
+ Commons Attribution 3.0</a> license:</p>
<blockquote><em>The Android robot is reproduced or modified from work created and shared by Google and
used according to terms described in the Creative Commons 3.0 Attribution License.</em></blockquote>
- <p>You may not file trademark applications incorporating the Android robot logo or
-derivatives thereof. We want to ensure that the Android robot remains available
-for all to use.</p>
+ <p>You may not file trademark applications incorporating the Android robot
+ logo or derivatives thereof within your company logo or business name. We
+ want to ensure that the Android robot remains available for all to use.</p>
<h4 style="clear:right">Android logo</h4>
@@ -78,12 +85,10 @@
<img alt="" src="{@docRoot}images/brand/android_logo_no.png">
</div>
-<p>The Android logo may not be used. Nor can this be used with the Android robot.</p>
+<p>The Android logo may not be used.</p>
+
<p>The custom typeface may not be used.</p>
-
-
-
<h2 id="brand-google_play">Google Play</h2>
@@ -98,7 +103,7 @@
<p>When referring to the mobile experience, use "Google Play" unless the text is clearly
instructional for the user. For example, a marketing headline might read "Download our
games on Google Play™," but instructional text would read "Download our games using the Google
-Play™ Store app."
+Play™ store app."
<p>Any use of the Google Play name or icon needs to include this
attribution in your communication:</p>
@@ -111,16 +116,16 @@
<p style="text-align:center">
<a href="{@docRoot}images/brand/Google_Play_Store_48.png">48x48</a> |
<a href="{@docRoot}images/brand/Google_Play_Store_96.png">96x96</a><br>
- <a href="{@docRoot}downloads/brand/Google_Play_Store.ai">Illustrator (.ai)</a>
+ <a href="{@docRoot}images/brand/Google_Play_Store_600.png">600x576</a>
</p>
</div>
-<h4>Google Play Store icon</h4>
+<h4>Google Play store icon</h4>
-<p>You may use the Google Play Store icon, but you may not modify it.</p>
+<p>You may use the Google Play store icon, but you may not modify it.</p>
-<p>As mentioned above, when referring to the Google Play Store app in copy, use the full name:
-"Google Play Store." However, when labeling the Google Play Store icon directly, it's OK to use
+<p>As mentioned above, when referring to the Google Play store app in copy, use the full name:
+"Google Play store." However, when labeling the Google Play store icon directly, it's OK to use
"Play Store" alone to accurately reflect the icon label as it appears on a device.</p>
@@ -140,9 +145,14 @@
<a href="{@docRoot}images/brand/en_generic_rgb_wo_60.png">172x60</a></p>
</div>
- <p>The "Get it on Google Play" and "Android App on Google Play" logos are badges that you
- can use on your website and promotional materials, to point to your products on Google
- Play.</p>
+ <p>The "Get it on Google Play" and "Android App on Google Play" logos are
+ badges that you can use on your website and promotional materials, to point
+ to your products on Google Play. Additional Google Play badge formats and
+ badges for music, books, magazines, movies, and TV shows are also available.
+ Use the <a
+ href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android
+ and Google Play Brand Permissions Inquiry form</a> to request
+ those badges.</p>
<ul>
<li>Don't modify the color, proportions, spacing, or any other aspect of the badge image.
@@ -163,7 +173,7 @@
<p>To quickly create a badge that links to your apps on Google Play,
use the <a
- href="{@docRoot}distribute/tools/promote/badges.html">Googe Play badge generator</a>
+ href="{@docRoot}distribute/tools/promote/badges.html">Google Play badge generator</a>
(provides the badge in over 40 languages).</p>
<p>To create your own size, download an Adobe® Illustrator® (.ai) file for the
@@ -171,25 +181,11 @@
badge in over 40 languages</a>.</p>
<p>For details on all the ways that you can link to your product details page in Google Play,
- see <a href="{@docRoot}distribute/tools/promote/linking.html">Linking to your products</a></p>
+ see <a href="{@docRoot}distribute/tools/promote/linking.html">Linking to your products</a>.</p>
+<h2 id="Marketing_Review">Marketing Reviews and Brand Inquiries</h2>
-
-<h2 id="Questions">Questions</h2>
-
-<p>To view our full guidelines or for any further brand usage questions, please contact our
-Android Partner Marketing team:</p>
-<ul>
- <li>For North and South America, please contact <a
- href="mailto:android-brand-approvals@google.com?Subject=Brand%20Approval%20Questions"
- >android-brand-approvals@google.com</a></li>
-
- <li>For Europe and Emerging Markets, please contact <a
- href="mailto:emea-android-brand@google.com?Subject=Brand%20Approval%20Questions"
- >emea-android-brand@google.com</a></li>
-
- <li>For Asia and Pacific-America, please contact <a
- href="mailto:apac-android-brand-approvals@google.com?Subject=Brand%20Approval%20Questions"
- >apac-android-brand-approvals@google.com</a></li>
-</ul>
-
+<p>Use the <a
+href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android
+and Google Play Brand Permissions Inquiry form</a> to submit any marketing
+reviews or brand inquires. Typical response time is at least one week.</p>
diff --git a/docs/html/guide/topics/ui/settings.jd b/docs/html/guide/topics/ui/settings.jd
index 1d36430..f454c4e 100644
--- a/docs/html/guide/topics/ui/settings.jd
+++ b/docs/html/guide/topics/ui/settings.jd
@@ -820,7 +820,8 @@
public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType";
...
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+ String key) {
if (key.equals(KEY_PREF_SYNC_CONN)) {
Preference connectionPref = findPreference(key);
// Set summary to be the user-description for the selected value
@@ -863,7 +864,40 @@
}
</pre>
+<p class="caution"><strong>Caution:</strong> When you call {@link
+android.content.SharedPreferences#registerOnSharedPreferenceChangeListener
+registerOnSharedPreferenceChangeListener()}, the preference manager does not
+currently store a strong reference to the listener. You must store a strong
+reference to the listener, or it will be susceptible to garbage collection. We
+recommend you keep a reference to the listener in the instance data of an object
+that will exist as long as you need the listener.</p>
+<p>For example, in the following code, the caller does not keep a reference to
+the listener. As a result, the listener will be subject to garbage collection,
+and it will fail at some indeterminate time in the future:</p>
+
+<pre>
+prefs.registerOnSharedPreferenceChangeListener(
+ // Bad! The listener is subject to garbage collection!
+ new SharedPreferences.OnSharedPreferenceChangeListener() {
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ // listener implementation
+ }
+});
+</pre>
+
+<p>Instead, store a reference to the listener in an instance data field of an
+object that will exist as long as the listener is needed:</p>
+
+<pre>
+SharedPreferences.OnSharedPreferenceChangeListener listener =
+ new SharedPreferences.OnSharedPreferenceChangeListener() {
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ // listener implementation
+ }
+};
+prefs.registerOnSharedPreferenceChangeListener(listener);
+</pre>
<h2 id="NetworkUsage">Managing Network Usage</h2>
@@ -1142,13 +1176,15 @@
final Parcelable superState = super.onSaveInstanceState();
// Check whether this Preference is persistent (continually saved)
if (isPersistent()) {
- // No need to save instance state since it's persistent, use superclass state
+ // No need to save instance state since it's persistent,
+ // use superclass state
return superState;
}
// Create instance of custom BaseSavedState
final SavedState myState = new SavedState(superState);
- // Set the state's value with the class member that holds current setting value
+ // Set the state's value with the class member that holds current
+ // setting value
myState.value = mNewValue;
return myState;
}
diff --git a/docs/html/images/brand/Google_Play_Store_600.png b/docs/html/images/brand/Google_Play_Store_600.png
new file mode 100644
index 0000000..f748652
--- /dev/null
+++ b/docs/html/images/brand/Google_Play_Store_600.png
Binary files differ
diff --git a/docs/image_sources/brand/Google_Play_Store.ai b/docs/image_sources/brand/Google_Play_Store.ai
deleted file mode 100644
index 51f07c6..0000000
--- a/docs/image_sources/brand/Google_Play_Store.ai
+++ /dev/null
Binary files differ
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index b7673d8..3ab57c1 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -44,7 +44,6 @@
mTileY = tileY;
final long b = bitmap.ni();
native_instance = nativeCreate(b, tileX.nativeInt, tileY.nativeInt);
- native_shader = nativePostCreate(native_instance, b, tileX.nativeInt, tileY.nativeInt);
}
/**
@@ -59,6 +58,4 @@
private static native long nativeCreate(long native_bitmap, int shaderTileModeX,
int shaderTileModeY);
- private static native long nativePostCreate(long native_shader, long native_bitmap,
- int shaderTileModeX, int shaderTileModeY);
}
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index 5109ffd..d7b2071 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -55,14 +55,6 @@
mXferMode = mode;
native_instance = nativeCreate1(shaderA.native_instance, shaderB.native_instance,
(mode != null) ? mode.native_instance : 0);
- if (mode instanceof PorterDuffXfermode) {
- PorterDuff.Mode pdMode = ((PorterDuffXfermode) mode).mode;
- native_shader = nativePostCreate2(native_instance, shaderA.native_shader,
- shaderB.native_shader, pdMode != null ? pdMode.nativeInt : 0);
- } else {
- native_shader = nativePostCreate1(native_instance, shaderA.native_shader,
- shaderB.native_shader, mode != null ? mode.native_instance : 0);
- }
}
/** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
@@ -79,8 +71,6 @@
mPorterDuffMode = mode;
native_instance = nativeCreate2(shaderA.native_instance, shaderB.native_instance,
mode.nativeInt);
- native_shader = nativePostCreate2(native_instance, shaderA.native_shader,
- shaderB.native_shader, mode.nativeInt);
}
/**
@@ -108,8 +98,4 @@
long native_mode);
private static native long nativeCreate2(long native_shaderA, long native_shaderB,
int porterDuffMode);
- private static native long nativePostCreate1(long native_shader, long native_skiaShaderA,
- long native_skiaShaderB, long native_mode);
- private static native long nativePostCreate2(long native_shader, long native_skiaShaderA,
- long native_skiaShaderB, int porterDuffMode);
}
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 0eae67c..90cb217 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -66,8 +66,6 @@
mPositions = positions;
mTileMode = tile;
native_instance = nativeCreate1(x0, y0, x1, y1, colors, positions, tile.nativeInt);
- native_shader = nativePostCreate1(native_instance, x0, y0, x1, y1, colors, positions,
- tile.nativeInt);
}
/** Create a shader that draws a linear gradient along a line.
@@ -90,8 +88,6 @@
mColor1 = color1;
mTileMode = tile;
native_instance = nativeCreate2(x0, y0, x1, y1, color0, color1, tile.nativeInt);
- native_shader = nativePostCreate2(native_instance, x0, y0, x1, y1, color0, color1,
- tile.nativeInt);
}
/**
@@ -120,8 +116,4 @@
int colors[], float positions[], int tileMode);
private native long nativeCreate2(float x0, float y0, float x1, float y1,
int color0, int color1, int tileMode);
- private native long nativePostCreate1(long native_shader, float x0, float y0, float x1, float y1,
- int colors[], float positions[], int tileMode);
- private native long nativePostCreate2(long native_shader, float x0, float y0, float x1, float y1,
- int color0, int color1, int tileMode);
}
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index c00c612..75c951a 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -66,8 +66,6 @@
mPositions = positions;
mTileMode = tile;
native_instance = nativeCreate1(x, y, radius, colors, positions, tile.nativeInt);
- native_shader = nativePostCreate1(native_instance, x, y, radius, colors, positions,
- tile.nativeInt);
}
/** Create a shader that draws a radial gradient given the center and radius.
@@ -91,8 +89,6 @@
mColor1 = color1;
mTileMode = tile;
native_instance = nativeCreate2(x, y, radius, color0, color1, tile.nativeInt);
- native_shader = nativePostCreate2(native_instance, x, y, radius, color0, color1,
- tile.nativeInt);
}
/**
@@ -121,10 +117,5 @@
int colors[], float positions[], int tileMode);
private static native long nativeCreate2(float x, float y, float radius,
int color0, int color1, int tileMode);
-
- private static native long nativePostCreate1(long native_shader, float x, float y, float radius,
- int colors[], float positions[], int tileMode);
- private static native long nativePostCreate2(long native_shader, float x, float y, float radius,
- int color0, int color1, int tileMode);
}
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 94b4c4a..6870ab4 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -29,10 +29,6 @@
* @hide
*/
public long native_instance;
- /**
- * @hide
- */
- public long native_shader;
private Matrix mLocalMatrix;
@@ -78,7 +74,7 @@
*/
public void setLocalMatrix(Matrix localM) {
mLocalMatrix = localM;
- nativeSetLocalMatrix(native_instance, native_shader,
+ nativeSetLocalMatrix(native_instance,
localM == null ? 0 : localM.native_instance);
}
@@ -86,7 +82,7 @@
try {
super.finalize();
} finally {
- nativeDestructor(native_instance, native_shader);
+ nativeDestructor(native_instance);
}
}
@@ -112,7 +108,7 @@
}
}
- private static native void nativeDestructor(long native_shader, long native_skiaShader);
+ private static native void nativeDestructor(long native_shader);
private static native void nativeSetLocalMatrix(long native_shader,
- long native_skiaShader, long matrix_instance);
+ long matrix_instance);
}
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index 21239f7..18a748f 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -63,7 +63,6 @@
mColors = colors;
mPositions = positions;
native_instance = nativeCreate1(cx, cy, colors, positions);
- native_shader = nativePostCreate1(native_instance, cx, cy, colors, positions);
}
/**
@@ -81,7 +80,6 @@
mColor0 = color0;
mColor1 = color1;
native_instance = nativeCreate2(cx, cy, color0, color1);
- native_shader = nativePostCreate2(native_instance, cx, cy, color0, color1);
}
/**
@@ -108,10 +106,5 @@
private static native long nativeCreate1(float x, float y, int colors[], float positions[]);
private static native long nativeCreate2(float x, float y, int color0, int color1);
-
- private static native long nativePostCreate1(long native_shader, float cx, float cy,
- int[] colors, float[] positions);
- private static native long nativePostCreate2(long native_shader, float cx, float cy,
- int color0, int color1);
}
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 46e3401..42872e9 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -80,6 +80,22 @@
this(null, null);
}
+ @Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ final boolean changed = super.setVisible(visible, restart);
+ if (mAnim != null) {
+ if (visible) {
+ if (changed || restart) {
+ // TODO: Should this support restart?
+ mAnim.end();
+ }
+ } else {
+ mAnim.end();
+ }
+ }
+ return changed;
+ }
+
/**
* Add a new drawable to the set of keyframes.
*
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index f3fcf2c..c95ac82 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -25,6 +25,7 @@
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
@@ -92,6 +93,9 @@
private int mBitmapWidth;
private int mBitmapHeight;
+ /** Optical insets due to gravity. */
+ private Insets mOpticalInsets = null;
+
// Mirroring matrix for using with Shaders
private Matrix mMirrorMatrix;
@@ -456,9 +460,9 @@
@Override
protected void onBoundsChange(Rect bounds) {
- super.onBoundsChange(bounds);
mApplyGravity = true;
- Shader shader = mBitmapState.mPaint.getShader();
+
+ final Shader shader = mBitmapState.mPaint.getShader();
if (shader != null) {
if (needMirroring()) {
updateMirrorMatrix(bounds.right - bounds.left);
@@ -517,9 +521,7 @@
final boolean needMirroring = needMirroring();
if (shader == null) {
if (mApplyGravity) {
- final int layoutDirection = getLayoutDirection();
- Gravity.apply(state.mGravity, mBitmapWidth, mBitmapHeight,
- getBounds(), mDstRect, layoutDirection);
+ applyGravity();
mApplyGravity = false;
}
@@ -564,6 +566,31 @@
}
}
+ /**
+ * @hide
+ */
+ @Override
+ public Insets getOpticalInsets() {
+ if (mApplyGravity && mBitmapState.mPaint.getShader() == null) {
+ applyGravity();
+ mApplyGravity = false;
+ }
+ return mOpticalInsets == null ? Insets.NONE : mOpticalInsets;
+ }
+
+ private void applyGravity() {
+ final Rect bounds = getBounds();
+ final int layoutDirection = getLayoutDirection();
+ Gravity.apply(mBitmapState.mGravity, mBitmapWidth, mBitmapHeight,
+ bounds, mDstRect, layoutDirection);
+
+ final int left = mDstRect.left - bounds.left;
+ final int top = mDstRect.top - bounds.top;
+ final int right = bounds.right - mDstRect.right;
+ final int bottom = bounds.bottom - mDstRect.bottom;
+ mOpticalInsets = Insets.of(left, top, right, bottom);
+ }
+
@Override
public void setAlpha(int alpha) {
final int oldAlpha = mBitmapState.mPaint.getAlpha();
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 04373d4..2aef39f 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -187,6 +187,9 @@
}
if (mCurrDrawable != null) {
mCurrDrawable.setBounds(bounds);
+
+ // Must obtain optical insets after setting bounds.
+ mInsets = mCurrDrawable.getOpticalInsets();
}
}
@@ -385,7 +388,6 @@
mCurrDrawable = d;
mCurIndex = idx;
if (d != null) {
- mInsets = d.getOpticalInsets();
d.mutate();
if (mDrawableContainerState.mEnterFadeDuration > 0) {
mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;
@@ -402,6 +404,9 @@
d.setBounds(getBounds());
d.setLayoutDirection(getLayoutDirection());
d.setAutoMirrored(mDrawableContainerState.mAutoMirrored);
+
+ // Must obtain optical insets after setting bounds.
+ mInsets = d.getOpticalInsets();
} else {
mInsets = Insets.NONE;
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index e3ed75e..e2bd50d 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -159,7 +159,8 @@
@Override
public void setColorFilter(ColorFilter colorFilter) {
- // TODO: support color filter
+ mVectorState.mVPathRenderer.setColorFilter(colorFilter);
+ invalidateSelf();
}
@Override
@@ -365,14 +366,15 @@
private VPath[] mCurrentPaths;
private Paint mStrokePaint;
private Paint mFillPaint;
+ private ColorFilter mColorFilter;
private PathMeasure mPathMeasure;
private VGroup mCurrentGroup = new VGroup();
- float mBaseWidth = 1;
- float mBaseHeight = 1;
- float mViewportWidth;
- float mViewportHeight;
+ float mBaseWidth = 0;
+ float mBaseHeight = 0;
+ float mViewportWidth = 0;
+ float mViewportHeight = 0;
public VPathRenderer() {
}
@@ -413,6 +415,18 @@
}
}
+ public void setColorFilter(ColorFilter colorFilter) {
+ mColorFilter = colorFilter;
+
+ if (mFillPaint != null) {
+ mFillPaint.setColorFilter(colorFilter);
+ }
+
+ if (mStrokePaint != null) {
+ mStrokePaint.setColorFilter(colorFilter);
+ }
+ }
+
public void draw(Canvas canvas, int w, int h) {
if (mCurrentPaths == null) {
Log.e(LOGTAG,"mCurrentPaths == null");
@@ -470,6 +484,7 @@
if (vPath.mFillColor != 0) {
if (mFillPaint == null) {
mFillPaint = new Paint();
+ mFillPaint.setColorFilter(mColorFilter);
mFillPaint.setStyle(Paint.Style.FILL);
mFillPaint.setAntiAlias(true);
}
@@ -481,6 +496,7 @@
if (vPath.mStrokeColor != 0) {
if (mStrokePaint == null) {
mStrokePaint = new Paint();
+ mStrokePaint.setColorFilter(mColorFilter);
mStrokePaint.setStyle(Paint.Style.STROKE);
mStrokePaint.setAntiAlias(true);
}
@@ -516,24 +532,34 @@
private void parseViewport(Resources r, AttributeSet attrs)
throws XmlPullParserException {
final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableViewport);
- mViewportWidth = a.getFloat(R.styleable.VectorDrawableViewport_viewportWidth, 0);
- mViewportHeight = a.getFloat(R.styleable.VectorDrawableViewport_viewportHeight, 0);
- if (mViewportWidth == 0 || mViewportHeight == 0) {
- throw new XmlPullParserException(a.getPositionDescription()+
- "<viewport> tag requires viewportWidth & viewportHeight to be set");
+ mViewportWidth = a.getFloat(R.styleable.VectorDrawableViewport_viewportWidth, mViewportWidth);
+ mViewportHeight = a.getFloat(R.styleable.VectorDrawableViewport_viewportHeight, mViewportHeight);
+
+ if (mViewportWidth <= 0) {
+ throw new XmlPullParserException(a.getPositionDescription() +
+ "<viewport> tag requires viewportWidth > 0");
+ } else if (mViewportHeight <= 0) {
+ throw new XmlPullParserException(a.getPositionDescription() +
+ "<viewport> tag requires viewportHeight > 0");
}
+
a.recycle();
}
private void parseSize(Resources r, AttributeSet attrs)
throws XmlPullParserException {
final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableSize);
- mBaseWidth = a.getDimension(R.styleable.VectorDrawableSize_width, 0);
- mBaseHeight = a.getDimension(R.styleable.VectorDrawableSize_height, 0);
- if (mBaseWidth == 0 || mBaseHeight == 0) {
- throw new XmlPullParserException(a.getPositionDescription()+
- "<size> tag requires width & height to be set");
+ mBaseWidth = a.getDimension(R.styleable.VectorDrawableSize_width, mBaseWidth);
+ mBaseHeight = a.getDimension(R.styleable.VectorDrawableSize_height, mBaseHeight);
+
+ if (mBaseWidth <= 0) {
+ throw new XmlPullParserException(a.getPositionDescription() +
+ "<size> tag requires width > 0");
+ } else if (mBaseHeight <= 0) {
+ throw new XmlPullParserException(a.getPositionDescription() +
+ "<size> tag requires height > 0");
}
+
a.recycle();
}
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 2e2ee15..5367663 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -33,6 +33,7 @@
#include "thread/TaskManager.h"
#include "AssetAtlas.h"
+#include "Extensions.h"
#include "FontRenderer.h"
#include "GammaFontRenderer.h"
#include "TextureCache.h"
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 3016814..937bf8d 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -230,6 +230,11 @@
return false;
}
+ if (op->mPaint && mOps[0].op->mPaint &&
+ op->mPaint->getShader() != mOps[0].op->mPaint->getShader()) {
+ return false;
+ }
+
/* Draw Modifiers compatibility check
*
* Shadows are ignored, as only text uses them, and in that case they are drawn
@@ -244,7 +249,6 @@
*/
const DrawModifiers& lhsMod = lhs->mDrawModifiers;
const DrawModifiers& rhsMod = rhs->mDrawModifiers;
- if (lhsMod.mShader != rhsMod.mShader) return false;
// Draw filter testing expects bit fields to be clear if filter not set.
if (lhsMod.mHasDrawFilter != rhsMod.mHasDrawFilter) return false;
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index dac86cb..96c6292 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -58,11 +58,6 @@
caches.resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
}
- for (size_t i = 0; i < shaders.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(shaders.itemAt(i));
- caches.resourceCache.destructorLocked(shaders.itemAt(i));
- }
-
for (size_t i = 0; i < sourcePaths.size(); i++) {
caches.resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i));
}
@@ -92,7 +87,6 @@
bitmapResources.clear();
ownedBitmapResources.clear();
patchResources.clear();
- shaders.clear();
sourcePaths.clear();
paints.clear();
regions.clear();
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index b2ead5b..11e78b0 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -56,7 +56,6 @@
class OpenGLRenderer;
class Rect;
class Layer;
-class SkiaShader;
class ClipRectOp;
class SaveLayerOp;
@@ -127,7 +126,6 @@
SortedVector<const SkPath*> sourcePaths;
Vector<const SkRegion*> regions;
Vector<const SkMatrix*> matrices;
- Vector<SkiaShader*> shaders;
Vector<Layer*> layers;
uint32_t functorCount;
bool hasDrawOps;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index e4867220..ea3e7a8 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -208,9 +208,16 @@
if (!state.mMatrix.isSimple()) return false;
// check state/paint for transparency
- if (state.mDrawModifiers.mShader ||
- state.mAlpha != 1.0f ||
- (mPaint && mPaint->getAlpha() != 0xFF)) return false;
+ if (mPaint) {
+ if (mPaint->getShader() && !mPaint->getShader()->isOpaque()) {
+ return false;
+ }
+ if (mPaint->getAlpha() != 0xFF) {
+ return false;
+ }
+ }
+
+ if (state.mAlpha != 1.0f) return false;
SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
return (mode == SkXfermode::kSrcOver_Mode ||
@@ -592,37 +599,6 @@
const SkRegion* mRegion;
};
-class ResetShaderOp : public StateOp {
-public:
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
- renderer.resetShader();
- }
-
- virtual void output(int level, uint32_t logFlags) const {
- OP_LOGS("ResetShader");
- }
-
- virtual const char* name() { return "ResetShader"; }
-};
-
-class SetupShaderOp : public StateOp {
-public:
- SetupShaderOp(SkiaShader* shader)
- : mShader(shader) {}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
- renderer.setupShader(mShader);
- }
-
- virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("SetupShader, shader %p", mShader);
- }
-
- virtual const char* name() { return "SetupShader"; }
-
-private:
- SkiaShader* mShader;
-};
-
class ResetPaintFilterOp : public StateOp {
public:
virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 606c67e..229afdf 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -47,7 +47,6 @@
///////////////////////////////////////////////////////////////////////////////
DisplayListData* DisplayListRenderer::finishRecording() {
- mShaderMap.clear();
mPaintMap.clear();
mRegionMap.clear();
mPathMap.clear();
@@ -394,15 +393,6 @@
return DrawGlInfo::kStatusDone;
}
-void DisplayListRenderer::resetShader() {
- addStateOp(new (alloc()) ResetShaderOp());
-}
-
-void DisplayListRenderer::setupShader(SkiaShader* shader) {
- shader = refShader(shader);
- addStateOp(new (alloc()) SetupShaderOp(shader));
-}
-
void DisplayListRenderer::resetPaintFilter() {
addStateOp(new (alloc()) ResetPaintFilterOp());
}
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index d814111..f0ae00f 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -95,9 +95,6 @@
virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
// Misc - should be implemented with SkPaint inspection
- virtual void resetShader();
- virtual void setupShader(SkiaShader* shader);
-
virtual void resetPaintFilter();
virtual void setupPaintFilter(int clearBits, int setBits);
@@ -269,21 +266,6 @@
return bitmap;
}
- inline SkiaShader* refShader(SkiaShader* shader) {
- if (!shader) return NULL;
-
- SkiaShader* shaderCopy = mShaderMap.valueFor(shader);
- // TODO: We also need to handle generation ID changes in compose shaders
- if (shaderCopy == NULL || shaderCopy->getGenerationId() != shader->getGenerationId()) {
- shaderCopy = shader->copy();
- // replaceValueFor() performs an add if the entry doesn't exist
- mShaderMap.replaceValueFor(shader, shaderCopy);
- mDisplayListData->shaders.add(shaderCopy);
- mCaches.resourceCache.incrementRefcount(shaderCopy);
- }
- return shaderCopy;
- }
-
inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
mDisplayListData->patchResources.add(patch);
mCaches.resourceCache.incrementRefcount(patch);
@@ -293,7 +275,6 @@
DefaultKeyedVector<const SkPaint*, const SkPaint*> mPaintMap;
DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap;
DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
- DefaultKeyedVector<SkiaShader*, SkiaShader*> mShaderMap;
Caches& mCaches;
DisplayListData* mDisplayListData;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 647c281..bf0ab5c 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -73,7 +73,7 @@
}
}
renderer->setupDrawColorFilter(paint->getColorFilter());
- renderer->setupDrawShader();
+ renderer->setupDrawShader(paint->getShader());
renderer->setupDrawBlending(paint);
renderer->setupDrawProgram();
renderer->setupDrawModelView(kModelViewMode_Translate, false,
@@ -85,7 +85,7 @@
renderer->setupDrawTexture(0);
renderer->setupDrawPureColorUniforms();
renderer->setupDrawColorFilterUniforms(paint->getColorFilter());
- renderer->setupDrawShaderUniforms(pureTranslate);
+ renderer->setupDrawShaderUniforms(paint->getShader(), pureTranslate);
renderer->setupDrawTextGammaUniforms();
return NO_ERROR;
@@ -592,7 +592,7 @@
}
FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text,
- uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
+ uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) {
checkInit();
DropShadow image;
@@ -613,8 +613,9 @@
Rect bounds;
mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
- uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
- uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
+ uint32_t intRadius = Blur::convertRadiusToInt(radius);
+ uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
+ uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius;
uint32_t maxSize = Caches::getInstance().maxTextureSize;
if (paddedWidth > maxSize || paddedHeight > maxSize) {
@@ -635,8 +636,8 @@
memset(dataBuffer, 0, size);
- int penX = radius - bounds.left;
- int penY = radius - bounds.bottom;
+ int penX = intRadius - bounds.left;
+ int penY = intRadius - bounds.bottom;
if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
// text has non-whitespace, so draw and blur to create the shadow
@@ -727,9 +728,10 @@
}
}
-void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
+void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
+ uint32_t intRadius = Blur::convertRadiusToInt(radius);
#ifdef ANDROID_ENABLE_RENDERSCRIPT
- if (width * height * radius >= RS_MIN_INPUT_CUTOFF) {
+ if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) {
uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
if (mRs == 0) {
@@ -768,12 +770,12 @@
}
#endif
- float *gaussian = new float[2 * radius + 1];
- Blur::generateGaussianWeights(gaussian, radius);
+ float *gaussian = new float[2 * intRadius + 1];
+ Blur::generateGaussianWeights(gaussian, intRadius);
uint8_t* scratch = new uint8_t[width * height];
- Blur::horizontal(gaussian, radius, *image, scratch, width, height);
- Blur::vertical(gaussian, radius, scratch, *image, width, height);
+ Blur::horizontal(gaussian, intRadius, *image, scratch, width, height);
+ Blur::vertical(gaussian, intRadius, scratch, *image, width, height);
delete[] gaussian;
delete[] scratch;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 9259028..8ce22b0 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -129,7 +129,7 @@
// After renderDropShadow returns, the called owns the memory in DropShadow.image
// and is responsible for releasing it when it's done with it
DropShadow renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex,
- uint32_t len, int numGlyphs, uint32_t radius, const float* positions);
+ uint32_t len, int numGlyphs, float radius, const float* positions);
void setTextureFiltering(bool linearFiltering) {
mLinearFiltering = linearFiltering;
@@ -218,7 +218,7 @@
int32_t width, int32_t height);
// the input image handle may have its pointer replaced (to avoid copies)
- void blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius);
+ void blurImage(uint8_t** image, int32_t width, int32_t height, float radius);
};
}; // namespace uirenderer
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 691f1c9..71836dd5 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <SkCanvas.h>
+#include <SkShader.h>
#include <SkTypeface.h>
#include <utils/Log.h>
@@ -37,6 +38,7 @@
#include "PathTessellator.h"
#include "Properties.h"
#include "ShadowTessellator.h"
+#include "SkiaShader.h"
#include "utils/GLUtils.h"
#include "Vector.h"
#include "VertexBuffer.h"
@@ -1053,6 +1055,45 @@
#define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND)
+// This class is purely for inspection. It inherits from SkShader, but Skia does not know how to
+// use it. The OpenGLRenderer will look at it to find its Layer and whether it is opaque.
+class LayerShader : public SkShader {
+public:
+ LayerShader(Layer* layer, const SkMatrix* localMatrix)
+ : INHERITED(localMatrix)
+ , mLayer(layer) {
+ }
+
+ virtual bool asACustomShader(void** data) const {
+ if (data) {
+ *data = static_cast<void*>(mLayer);
+ }
+ return true;
+ }
+
+ virtual bool isOpaque() const {
+ return !mLayer->isBlend();
+ }
+
+protected:
+ virtual void shadeSpan(int x, int y, SkPMColor[], int count) {
+ LOG_ALWAYS_FATAL("LayerShader should never be drawn with raster backend.");
+ }
+
+ virtual void flatten(SkWriteBuffer&) const {
+ LOG_ALWAYS_FATAL("LayerShader should never be flattened.");
+ }
+
+ virtual Factory getFactory() const {
+ LOG_ALWAYS_FATAL("LayerShader should never be created from a stream.");
+ return NULL;
+ }
+private:
+ // Unowned.
+ Layer* mLayer;
+ typedef SkShader INHERITED;
+};
+
void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw
@@ -1066,21 +1107,19 @@
paint.setAntiAlias(true);
paint.setColor(SkColorSetARGB(int(getLayerAlpha(layer) * 255), 0, 0, 0));
- SkiaShader* oldShader = mDrawModifiers.mShader;
-
// create LayerShader to map SaveLayer content into subsequent draw
SkMatrix shaderMatrix;
shaderMatrix.setTranslate(rect.left, rect.bottom);
shaderMatrix.preScale(1, -1);
- SkiaLayerShader layerShader(layer, &shaderMatrix);
- mDrawModifiers.mShader = &layerShader;
+ LayerShader layerShader(layer, &shaderMatrix);
+ paint.setShader(&layerShader);
// Since the drawing primitive is defined in local drawing space,
// we don't need to modify the draw matrix
const SkPath* maskPath = layer->getConvexMask();
DRAW_DOUBLE_STENCIL(drawConvexPath(*maskPath, &paint));
- mDrawModifiers.mShader = oldShader;
+ paint.setShader(NULL);
restore();
return;
@@ -1627,9 +1666,9 @@
mSetShaderColor = mDescription.setColorModulate(a);
}
-void OpenGLRenderer::setupDrawShader() {
- if (mDrawModifiers.mShader) {
- mDrawModifiers.mShader->describe(mDescription, mExtensions);
+void OpenGLRenderer::setupDrawShader(const SkShader* shader) {
+ if (shader != NULL) {
+ SkiaShader::describe(&mCaches, mDescription, mExtensions, *shader);
}
}
@@ -1655,15 +1694,21 @@
}
}
+static bool isBlendedColorFilter(const SkColorFilter* filter) {
+ if (filter == NULL) {
+ return false;
+ }
+ return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
+}
+
void OpenGLRenderer::setupDrawBlending(const Layer* layer, bool swapSrcDst) {
SkXfermode::Mode mode = layer->getMode();
// When the blending mode is kClear_Mode, we need to use a modulate color
// argb=1,0,0,0
accountForClear(mode);
+ // TODO: check shader blending, once we have shader drawing support for layers.
bool blend = layer->isBlend() || getLayerAlpha(layer) < 1.0f ||
- (mColorSet && mColorA < 1.0f) ||
- (mDrawModifiers.mShader && mDrawModifiers.mShader->blend()) ||
- layer->getColorFilter();
+ (mColorSet && mColorA < 1.0f) || isBlendedColorFilter(layer->getColorFilter());
chooseBlending(blend, mode, mDescription, swapSrcDst);
}
@@ -1673,8 +1718,8 @@
// argb=1,0,0,0
accountForClear(mode);
blend |= (mColorSet && mColorA < 1.0f) ||
- (mDrawModifiers.mShader && mDrawModifiers.mShader->blend()) ||
- (paint && paint->getColorFilter());
+ (getShader(paint) && !getShader(paint)->isOpaque()) ||
+ isBlendedColorFilter(getColorFilter(paint));
chooseBlending(blend, mode, mDescription, swapSrcDst);
}
@@ -1717,8 +1762,8 @@
}
}
-void OpenGLRenderer::setupDrawColorUniforms() {
- if ((mColorSet && !mDrawModifiers.mShader) || (mDrawModifiers.mShader && mSetShaderColor)) {
+void OpenGLRenderer::setupDrawColorUniforms(bool hasShader) {
+ if ((mColorSet && !hasShader) || (hasShader && mSetShaderColor)) {
mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
}
}
@@ -1729,20 +1774,22 @@
}
}
-void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) {
- if (mDrawModifiers.mShader) {
- if (ignoreTransform) {
- // if ignoreTransform=true was passed to setupDrawModelView, undo currentTransform()
- // because it was built into modelView / the geometry, and the SkiaShader needs to
- // compensate.
- mat4 modelViewWithoutTransform;
- modelViewWithoutTransform.loadInverse(*currentTransform());
- modelViewWithoutTransform.multiply(mModelViewMatrix);
- mModelViewMatrix.load(modelViewWithoutTransform);
- }
- mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
- mModelViewMatrix, *mSnapshot, &mTextureUnit);
+void OpenGLRenderer::setupDrawShaderUniforms(const SkShader* shader, bool ignoreTransform) {
+ if (shader == NULL) {
+ return;
}
+
+ if (ignoreTransform) {
+ // if ignoreTransform=true was passed to setupDrawModelView, undo currentTransform()
+ // because it was built into modelView / the geometry, and the description needs to
+ // compensate.
+ mat4 modelViewWithoutTransform;
+ modelViewWithoutTransform.loadInverse(*currentTransform());
+ modelViewWithoutTransform.multiply(mModelViewMatrix);
+ mModelViewMatrix.load(modelViewWithoutTransform);
+ }
+
+ SkiaShader::setupProgram(&mCaches, mModelViewMatrix, &mTextureUnit, mExtensions, *shader);
}
void OpenGLRenderer::setupDrawColorFilterUniforms(const SkColorFilter* filter) {
@@ -2201,7 +2248,7 @@
// Apply a scale transform on the canvas only when a shader is in use
// Skia handles the ratio between the dst and src rects as a scale factor
// when a shader is set
- bool useScaleTransform = mDrawModifiers.mShader && scaled;
+ bool useScaleTransform = getShader(paint) && scaled;
bool ignoreTransform = false;
if (CC_LIKELY(currentTransform()->isPureTranslate() && !useScaleTransform)) {
@@ -2359,13 +2406,13 @@
if (isAA) setupDrawAA();
setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
setupDrawColorFilter(getColorFilter(paint));
- setupDrawShader();
+ setupDrawShader(getShader(paint));
setupDrawBlending(paint, isAA);
setupDrawProgram();
setupDrawModelView(kModelViewMode_Translate, useOffset, 0, 0, 0, 0);
- setupDrawColorUniforms();
+ setupDrawColorUniforms(getShader(paint));
setupDrawColorFilterUniforms(getColorFilter(paint));
- setupDrawShaderUniforms();
+ setupDrawShaderUniforms(getShader(paint));
const void* vertices = vertexBuffer.getBuffer();
bool force = mCaches.unbindMeshBuffer();
@@ -2670,7 +2717,7 @@
const float sy = y - shadow->top + textShadow.dy;
const int shadowAlpha = ((textShadow.color >> 24) & 0xFF) * mSnapshot->alpha;
- if (mDrawModifiers.mShader) {
+ if (getShader(paint)) {
textShadow.color = SK_ColorWHITE;
}
@@ -2678,7 +2725,7 @@
setupDrawWithTexture(true);
setupDrawAlpha8Color(textShadow.color, shadowAlpha < 255 ? shadowAlpha : alpha);
setupDrawColorFilter(getColorFilter(paint));
- setupDrawShader();
+ setupDrawShader(getShader(paint));
setupDrawBlending(paint, true);
setupDrawProgram();
setupDrawModelView(kModelViewMode_TranslateAndScale, false,
@@ -2686,7 +2733,7 @@
setupDrawTexture(shadow->id);
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms(getColorFilter(paint));
- setupDrawShaderUniforms();
+ setupDrawShaderUniforms(getShader(paint));
setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
@@ -3008,21 +3055,6 @@
}
///////////////////////////////////////////////////////////////////////////////
-// Shaders
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::resetShader() {
- mDrawModifiers.mShader = NULL;
-}
-
-void OpenGLRenderer::setupShader(SkiaShader* shader) {
- mDrawModifiers.mShader = shader;
- if (mDrawModifiers.mShader) {
- mDrawModifiers.mShader->setCaches(mCaches);
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
// Draw filters
///////////////////////////////////////////////////////////////////////////////
@@ -3080,7 +3112,7 @@
setupDrawWithTexture(true);
setupDrawAlpha8Color(paint->getColor(), alpha);
setupDrawColorFilter(getColorFilter(paint));
- setupDrawShader();
+ setupDrawShader(getShader(paint));
setupDrawBlending(paint, true);
setupDrawProgram();
setupDrawModelView(kModelViewMode_TranslateAndScale, false,
@@ -3088,7 +3120,7 @@
setupDrawTexture(texture->id);
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms(getColorFilter(paint));
- setupDrawShaderUniforms();
+ setupDrawShaderUniforms(getShader(paint));
setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
@@ -3254,7 +3286,7 @@
int color = paint->getColor();
// If a shader is set, preserve only the alpha
- if (mDrawModifiers.mShader) {
+ if (getShader(paint)) {
color |= 0x00ffffff;
}
@@ -3290,15 +3322,15 @@
setupDraw();
setupDrawNoTexture();
setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha);
- setupDrawShader();
+ setupDrawShader(getShader(paint));
setupDrawColorFilter(getColorFilter(paint));
setupDrawBlending(paint);
setupDrawProgram();
setupDrawDirtyRegionsDisabled();
setupDrawModelView(kModelViewMode_Translate, false,
0.0f, 0.0f, 0.0f, 0.0f, ignoreTransform);
- setupDrawColorUniforms();
- setupDrawShaderUniforms();
+ setupDrawColorUniforms(getShader(paint));
+ setupDrawShaderUniforms(getShader(paint));
setupDrawColorFilterUniforms(getColorFilter(paint));
if (dirty && hasLayer()) {
@@ -3314,21 +3346,21 @@
const SkPaint* paint, bool ignoreTransform) {
int color = paint->getColor();
// If a shader is set, preserve only the alpha
- if (mDrawModifiers.mShader) {
+ if (getShader(paint)) {
color |= 0x00ffffff;
}
setupDraw();
setupDrawNoTexture();
setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha);
- setupDrawShader();
+ setupDrawShader(getShader(paint));
setupDrawColorFilter(getColorFilter(paint));
setupDrawBlending(paint);
setupDrawProgram();
setupDrawModelView(kModelViewMode_TranslateAndScale, false,
left, top, right, bottom, ignoreTransform);
- setupDrawColorUniforms();
- setupDrawShaderUniforms(ignoreTransform);
+ setupDrawColorUniforms(getShader(paint));
+ setupDrawShaderUniforms(getShader(paint), ignoreTransform);
setupDrawColorFilterUniforms(getColorFilter(paint));
setupDrawSimpleMesh();
@@ -3441,7 +3473,7 @@
setupDrawAlpha8Color(color, alpha);
}
setupDrawColorFilter(getColorFilter(paint));
- setupDrawShader();
+ setupDrawShader(getShader(paint));
setupDrawBlending(paint, true);
setupDrawProgram();
if (!dirty) setupDrawDirtyRegionsDisabled();
@@ -3449,7 +3481,7 @@
setupDrawTexture(texture);
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms(getColorFilter(paint));
- setupDrawShaderUniforms(ignoreTransform);
+ setupDrawShaderUniforms(getShader(paint), ignoreTransform);
setupDrawMesh(vertices, texCoords);
glDrawArrays(drawMode, 0, elementsCount);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index c6d9071..fc27947 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -26,7 +26,6 @@
#include <SkMatrix.h>
#include <SkPaint.h>
#include <SkRegion.h>
-#include <SkShader.h>
#include <SkXfermode.h>
#include <utils/Blur.h>
@@ -45,13 +44,15 @@
#include "Program.h"
#include "Rect.h"
#include "Renderer.h"
-#include "StatefulBaseRenderer.h"
#include "Snapshot.h"
+#include "StatefulBaseRenderer.h"
#include "UvMapper.h"
#include "Vertex.h"
#include "Caches.h"
#include "CanvasProperty.h"
+class SkShader;
+
namespace android {
namespace uirenderer {
@@ -59,7 +60,6 @@
class RenderNode;
class TextSetupFunctor;
class VertexBuffer;
-class SkiaShader;
struct DrawModifiers {
DrawModifiers() {
@@ -70,7 +70,6 @@
memset(this, 0, sizeof(DrawModifiers));
}
- SkiaShader* mShader;
float mOverrideLayerAlpha;
// Draw filters
@@ -217,9 +216,6 @@
status_t drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
float casterAlpha, bool casterUnclipped, const SkPath* casterPerimeter);
- virtual void resetShader();
- virtual void setupShader(SkiaShader* shader);
-
virtual void resetPaintFilter();
virtual void setupPaintFilter(int clearBits, int setBits);
@@ -467,6 +463,14 @@
}
/**
+ * Safely retrieves the Shader from the given Paint. If the paint is
+ * null then null is returned.
+ */
+ static inline const SkShader* getShader(const SkPaint* paint) {
+ return paint ? paint->getShader() : NULL;
+ }
+
+ /**
* Set to true to suppress error checks at the end of a frame.
*/
virtual bool suppressErrorChecks() const {
@@ -838,7 +842,7 @@
void setupDrawColor(float r, float g, float b, float a);
void setupDrawAlpha8Color(int color, int alpha);
void setupDrawTextGamma(const SkPaint* paint);
- void setupDrawShader();
+ void setupDrawShader(const SkShader* shader);
void setupDrawColorFilter(const SkColorFilter* filter);
void setupDrawBlending(const Layer* layer, bool swapSrcDst = false);
void setupDrawBlending(const SkPaint* paint, bool blend = true, bool swapSrcDst = false);
@@ -862,9 +866,17 @@
*/
void setupDrawModelView(ModelViewMode mode, bool offset,
float left, float top, float right, float bottom, bool ignoreTransform = false);
- void setupDrawColorUniforms();
+ void setupDrawColorUniforms(bool hasShader);
void setupDrawPureColorUniforms();
- void setupDrawShaderUniforms(bool ignoreTransform = false);
+
+ /**
+ * Setup uniforms for the current shader.
+ *
+ * @param shader SkShader on the current paint.
+ *
+ * @param ignoreTransform Set to true to ignore the transform in shader.
+ */
+ void setupDrawShaderUniforms(const SkShader* shader, bool ignoreTransform = false);
void setupDrawColorFilterUniforms(const SkColorFilter* paint);
void setupDrawSimpleMesh();
void setupDrawTexture(GLuint texture);
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index e191a26..23cab0e 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -35,7 +35,6 @@
class Layer;
class Matrix4;
class SkiaColorFilter;
-class SkiaShader;
class Patch;
enum DrawOpMode {
@@ -183,9 +182,6 @@
virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
// Misc - should be implemented with SkPaint inspection
- virtual void resetShader() = 0;
- virtual void setupShader(SkiaShader* shader) = 0;
-
virtual void resetPaintFilter() = 0;
virtual void setupPaintFilter(int clearBits, int setBits) = 0;
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 13a3e8e..8b553d1 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -71,11 +71,6 @@
incrementRefcount((void*) pathResource, kPath);
}
-void ResourceCache::incrementRefcount(SkiaShader* shaderResource) {
- SkSafeRef(shaderResource->getSkShader());
- incrementRefcount((void*) shaderResource, kShader);
-}
-
void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) {
incrementRefcount((void*) patchResource, kNinePatch);
}
@@ -104,11 +99,6 @@
incrementRefcountLocked((void*) pathResource, kPath);
}
-void ResourceCache::incrementRefcountLocked(SkiaShader* shaderResource) {
- SkSafeRef(shaderResource->getSkShader());
- incrementRefcountLocked((void*) shaderResource, kShader);
-}
-
void ResourceCache::incrementRefcountLocked(const Res_png_9patch* patchResource) {
incrementRefcountLocked((void*) patchResource, kNinePatch);
}
@@ -132,11 +122,6 @@
decrementRefcount((void*) pathResource);
}
-void ResourceCache::decrementRefcount(SkiaShader* shaderResource) {
- SkSafeUnref(shaderResource->getSkShader());
- decrementRefcount((void*) shaderResource);
-}
-
void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
decrementRefcount((void*) patchResource);
}
@@ -168,11 +153,6 @@
decrementRefcountLocked((void*) pathResource);
}
-void ResourceCache::decrementRefcountLocked(SkiaShader* shaderResource) {
- SkSafeUnref(shaderResource->getSkShader());
- decrementRefcountLocked((void*) shaderResource);
-}
-
void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) {
decrementRefcountLocked((void*) patchResource);
}
@@ -227,25 +207,6 @@
}
}
-void ResourceCache::destructor(SkiaShader* resource) {
- Mutex::Autolock _l(mLock);
- destructorLocked(resource);
-}
-
-void ResourceCache::destructorLocked(SkiaShader* resource) {
- ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
- if (ref == NULL) {
- // If we're not tracking this resource, just delete it
- delete resource;
- return;
- }
- ref->destroyed = true;
- if (ref->refCount == 0) {
- deleteResourceReferenceLocked(resource, ref);
- }
-}
-
void ResourceCache::destructor(Res_png_9patch* resource) {
Mutex::Autolock _l(mLock);
destructorLocked(resource);
@@ -333,11 +294,6 @@
}
}
break;
- case kShader: {
- SkiaShader* shader = (SkiaShader*) resource;
- delete shader;
- }
- break;
case kNinePatch: {
if (Caches::hasInstance()) {
Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource);
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index 4097ba4..3864d4b 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -20,7 +20,6 @@
#include <cutils/compiler.h>
#include <SkBitmap.h>
-#include <SkiaShader.h>
#include <utils/KeyedVector.h>
@@ -36,7 +35,6 @@
*/
enum ResourceType {
kBitmap,
- kShader,
kNinePatch,
kPath,
kLayer
@@ -70,36 +68,30 @@
void incrementRefcount(const SkPath* resource);
void incrementRefcount(const SkBitmap* resource);
- void incrementRefcount(SkiaShader* resource);
void incrementRefcount(const Res_png_9patch* resource);
void incrementRefcount(Layer* resource);
void incrementRefcountLocked(const SkPath* resource);
void incrementRefcountLocked(const SkBitmap* resource);
- void incrementRefcountLocked(SkiaShader* resource);
void incrementRefcountLocked(const Res_png_9patch* resource);
void incrementRefcountLocked(Layer* resource);
void decrementRefcount(const SkBitmap* resource);
void decrementRefcount(const SkPath* resource);
- void decrementRefcount(SkiaShader* resource);
void decrementRefcount(const Res_png_9patch* resource);
void decrementRefcount(Layer* resource);
void decrementRefcountLocked(const SkBitmap* resource);
void decrementRefcountLocked(const SkPath* resource);
- void decrementRefcountLocked(SkiaShader* resource);
void decrementRefcountLocked(const Res_png_9patch* resource);
void decrementRefcountLocked(Layer* resource);
void destructor(SkPath* resource);
void destructor(const SkBitmap* resource);
- void destructor(SkiaShader* resource);
void destructor(Res_png_9patch* resource);
void destructorLocked(SkPath* resource);
void destructorLocked(const SkBitmap* resource);
- void destructorLocked(SkiaShader* resource);
void destructorLocked(Res_png_9patch* resource);
bool recycle(SkBitmap* resource);
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 6a4a0c8..c672bc4 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -21,9 +21,10 @@
#include <SkMatrix.h>
#include "Caches.h"
+#include "Layer.h"
+#include "Matrix.h"
#include "SkiaShader.h"
#include "Texture.h"
-#include "Matrix.h"
namespace android {
namespace uirenderer {
@@ -54,89 +55,142 @@
a);
}
-///////////////////////////////////////////////////////////////////////////////
-// Base shader
-///////////////////////////////////////////////////////////////////////////////
-
-void SkiaShader::copyFrom(const SkiaShader& shader) {
- mType = shader.mType;
- mKey = shader.mKey;
- mTileX = shader.mTileX;
- mTileY = shader.mTileY;
- mBlend = shader.mBlend;
- mUnitMatrix = shader.mUnitMatrix;
- mShaderMatrix = shader.mShaderMatrix;
- mGenerationId = shader.mGenerationId;
-}
-
-SkiaShader::SkiaShader(): mCaches(NULL) {
-}
-
-SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
- SkShader::TileMode tileY, const SkMatrix* matrix, bool blend):
- mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend),
- mCaches(NULL) {
- setMatrix(matrix);
- mGenerationId = 0;
-}
-
-SkiaShader::~SkiaShader() {
-}
-
-void SkiaShader::describe(ProgramDescription& description, const Extensions& extensions) {
-}
-
-void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
- GLuint* textureUnit) {
-}
-
-void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT) {
- mCaches->bindTexture(texture->id);
+static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
+ caches->bindTexture(texture->id);
texture->setWrapST(wrapS, wrapT);
}
-void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView) {
- screenSpace.loadMultiply(mUnitMatrix, mShaderMatrix);
- screenSpace.multiply(modelView);
+/**
+ * Compute the matrix to transform to screen space.
+ * @param screenSpace Output param for the computed matrix.
+ * @param unitMatrix The unit matrix for gradient shaders, as returned by SkShader::asAGradient,
+ * or identity.
+ * @param localMatrix Local matrix, as returned by SkShader::getLocalMatrix().
+ * @param modelViewMatrix Model view matrix, as supplied by the OpenGLRenderer.
+ */
+static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatrix,
+ const SkMatrix& localMatrix, const mat4& modelViewMatrix) {
+ mat4 shaderMatrix;
+ // uses implicit construction
+ shaderMatrix.loadInverse(localMatrix);
+ // again, uses implicit construction
+ screenSpace.loadMultiply(unitMatrix, shaderMatrix);
+ screenSpace.multiply(modelViewMatrix);
+}
+
+// Returns true if one is a bitmap and the other is a gradient
+static bool bitmapAndGradient(SkiaShaderType type1, SkiaShaderType type2) {
+ return (type1 == kBitmap_SkiaShaderType && type2 == kGradient_SkiaShaderType)
+ || (type2 == kBitmap_SkiaShaderType && type1 == kGradient_SkiaShaderType);
+}
+
+SkiaShaderType SkiaShader::getType(const SkShader& shader) {
+ // First check for a gradient shader.
+ switch (shader.asAGradient(NULL)) {
+ case SkShader::kNone_GradientType:
+ // Not a gradient shader. Fall through to check for other types.
+ break;
+ case SkShader::kLinear_GradientType:
+ case SkShader::kRadial_GradientType:
+ case SkShader::kSweep_GradientType:
+ return kGradient_SkiaShaderType;
+ default:
+ // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
+ return kNone_SkiaShaderType;
+ }
+
+ // The shader is not a gradient. Check for a bitmap shader.
+ if (shader.asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
+ return kBitmap_SkiaShaderType;
+ }
+
+ // Check for a ComposeShader.
+ SkShader::ComposeRec rec;
+ if (shader.asACompose(&rec)) {
+ const SkiaShaderType shaderAType = getType(*rec.fShaderA);
+ const SkiaShaderType shaderBType = getType(*rec.fShaderB);
+
+ // Compose is only supported if one is a bitmap and the other is a
+ // gradient. Otherwise, return None to skip.
+ if (!bitmapAndGradient(shaderAType, shaderBType)) {
+ return kNone_SkiaShaderType;
+ }
+ return kCompose_SkiaShaderType;
+ }
+
+ if (shader.asACustomShader(NULL)) {
+ return kLayer_SkiaShaderType;
+ }
+
+ return kNone_SkiaShaderType;
+}
+
+typedef void (*describeProc)(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader);
+
+describeProc gDescribeProc[] = {
+ InvalidSkiaShader::describe,
+ SkiaBitmapShader::describe,
+ SkiaGradientShader::describe,
+ SkiaComposeShader::describe,
+ SkiaLayerShader::describe,
+};
+
+typedef void (*setupProgramProc)(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+
+setupProgramProc gSetupProgramProc[] = {
+ InvalidSkiaShader::setupProgram,
+ SkiaBitmapShader::setupProgram,
+ SkiaGradientShader::setupProgram,
+ SkiaComposeShader::setupProgram,
+ SkiaLayerShader::setupProgram,
+};
+
+void SkiaShader::describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader) {
+ gDescribeProc[getType(shader)](caches, description, extensions, shader);
+}
+
+void SkiaShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+
+ gSetupProgramProc[getType(shader)](caches, modelViewMatrix, textureUnit, extensions, shader);
}
///////////////////////////////////////////////////////////////////////////////
// Layer shader
///////////////////////////////////////////////////////////////////////////////
-SkiaLayerShader::SkiaLayerShader(Layer* layer, const SkMatrix* matrix):
- SkiaShader(kBitmap, NULL, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
- matrix, layer->isBlend()), mLayer(layer) {
- updateLocalMatrix(matrix);
-}
-
-SkiaShader* SkiaLayerShader::copy() {
- SkiaLayerShader* copy = new SkiaLayerShader();
- copy->copyFrom(*this);
- copy->mLayer = mLayer;
- return copy;
-}
-
-void SkiaLayerShader::describe(ProgramDescription& description, const Extensions& extensions) {
+void SkiaLayerShader::describe(Caches*, ProgramDescription& description,
+ const Extensions&, const SkShader& shader) {
description.hasBitmap = true;
}
-void SkiaLayerShader::setupProgram(Program* program, const mat4& modelView,
- const Snapshot& snapshot, GLuint* textureUnit) {
- GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().activeTexture(textureSlot);
+void SkiaLayerShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions&, const SkShader& shader) {
+ Layer* layer;
+ if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) {
+ LOG_ALWAYS_FATAL("SkiaLayerShader::setupProgram called on the wrong type of shader!");
+ }
- const float width = mLayer->getWidth();
- const float height = mLayer->getHeight();
+ GLuint textureSlot = (*textureUnit)++;
+ caches->activeTexture(textureSlot);
+
+ const float width = layer->getWidth();
+ const float height = layer->getHeight();
mat4 textureTransform;
- computeScreenSpaceMatrix(textureTransform, modelView);
+ computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
+ modelViewMatrix);
+
// Uniforms
- mLayer->bindTexture();
- mLayer->setWrap(GL_CLAMP_TO_EDGE);
- mLayer->setFilter(GL_LINEAR);
+ layer->bindTexture();
+ layer->setWrap(GL_CLAMP_TO_EDGE);
+ layer->setFilter(GL_LINEAR);
+ Program* program = caches->currentProgram;
glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
GL_FALSE, &textureTransform.data[0]);
@@ -147,67 +201,99 @@
// Bitmap shader
///////////////////////////////////////////////////////////////////////////////
-SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
- SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
- SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) {
- updateLocalMatrix(matrix);
-}
+struct BitmapShaderInfo {
+ float width;
+ float height;
+ GLenum wrapS;
+ GLenum wrapT;
+ Texture* texture;
+};
-SkiaShader* SkiaBitmapShader::copy() {
- SkiaBitmapShader* copy = new SkiaBitmapShader();
- copy->copyFrom(*this);
- copy->mBitmap = mBitmap;
- return copy;
-}
-
-void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
- Texture* texture = mCaches->textureCache.get(mBitmap);
- if (!texture) return;
- mTexture = texture;
+static bool bitmapShaderHelper(Caches* caches, ProgramDescription* description,
+ BitmapShaderInfo* shaderInfo,
+ const Extensions& extensions,
+ const SkBitmap& bitmap, SkShader::TileMode tileModes[2]) {
+ Texture* texture = caches->textureCache.get(&bitmap);
+ if (!texture) return false;
const float width = texture->width;
const float height = texture->height;
+ GLenum wrapS, wrapT;
- description.hasBitmap = true;
+ if (description) {
+ description->hasBitmap = true;
+ }
// The driver does not support non-power of two mirrored/repeated
// textures, so do it ourselves
if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
- (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) {
- description.isBitmapNpot = true;
- description.bitmapWrapS = gTileModes[mTileX];
- description.bitmapWrapT = gTileModes[mTileY];
- mWrapS = GL_CLAMP_TO_EDGE;
- mWrapT = GL_CLAMP_TO_EDGE;
+ (tileModes[0] != SkShader::kClamp_TileMode ||
+ tileModes[1] != SkShader::kClamp_TileMode)) {
+ if (description) {
+ description->isBitmapNpot = true;
+ description->bitmapWrapS = gTileModes[tileModes[0]];
+ description->bitmapWrapT = gTileModes[tileModes[1]];
+ }
+ wrapS = GL_CLAMP_TO_EDGE;
+ wrapT = GL_CLAMP_TO_EDGE;
} else {
- mWrapS = gTileModes[mTileX];
- mWrapT = gTileModes[mTileY];
+ wrapS = gTileModes[tileModes[0]];
+ wrapT = gTileModes[tileModes[1]];
}
+
+ if (shaderInfo) {
+ shaderInfo->width = width;
+ shaderInfo->height = height;
+ shaderInfo->wrapS = wrapS;
+ shaderInfo->wrapT = wrapT;
+ shaderInfo->texture = texture;
+ }
+ return true;
}
-void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
- const Snapshot&, GLuint* textureUnit) {
+void SkiaBitmapShader::describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader) {
+ SkBitmap bitmap;
+ SkShader::TileMode xy[2];
+ if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
+ LOG_ALWAYS_FATAL("SkiaBitmapShader::describe called with a different kind of shader!");
+ }
+ bitmapShaderHelper(caches, &description, NULL, extensions, bitmap, xy);
+}
+
+void SkiaBitmapShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+ SkBitmap bitmap;
+ SkShader::TileMode xy[2];
+ if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
+ LOG_ALWAYS_FATAL("SkiaBitmapShader::setupProgram called with a different kind of shader!");
+ }
+
GLuint textureSlot = (*textureUnit)++;
Caches::getInstance().activeTexture(textureSlot);
- Texture* texture = mTexture;
- mTexture = NULL;
- if (!texture) return;
+ BitmapShaderInfo shaderInfo;
+ if (!bitmapShaderHelper(caches, NULL, &shaderInfo, extensions, bitmap, xy)) {
+ return;
+ }
+
+ Program* program = caches->currentProgram;
+ Texture* texture = shaderInfo.texture;
+
const AutoTexture autoCleanup(texture);
- const float width = texture->width;
- const float height = texture->height;
-
mat4 textureTransform;
- computeScreenSpaceMatrix(textureTransform, modelView);
+ computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
+ modelViewMatrix);
// Uniforms
- bindTexture(texture, mWrapS, mWrapT);
+ bindTexture(caches, texture, shaderInfo.wrapS, shaderInfo.wrapT);
texture->setFilter(GL_LINEAR);
glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
GL_FALSE, &textureTransform.data[0]);
- glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
+ glUniform2f(program->getUniform("textureDimension"), 1.0f / shaderInfo.width,
+ 1.0f / shaderInfo.height);
}
///////////////////////////////////////////////////////////////////////////////
@@ -225,74 +311,6 @@
matrix->postScale(inv, inv);
}
-SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colors,
- float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
- SkMatrix* matrix, bool blend):
- SkiaShader(kLinearGradient, key, tileMode, tileMode, matrix, blend),
- mBounds(bounds), mColors(colors), mPositions(positions), mCount(count) {
- SkPoint points[2];
- points[0].set(bounds[0], bounds[1]);
- points[1].set(bounds[2], bounds[3]);
-
- SkMatrix unitMatrix;
- toUnitMatrix(points, &unitMatrix);
- mUnitMatrix.load(unitMatrix);
-
- updateLocalMatrix(matrix);
-
- mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
-}
-
-SkiaLinearGradientShader::~SkiaLinearGradientShader() {
- delete[] mBounds;
- delete[] mColors;
- delete[] mPositions;
-}
-
-SkiaShader* SkiaLinearGradientShader::copy() {
- SkiaLinearGradientShader* copy = new SkiaLinearGradientShader();
- copy->copyFrom(*this);
- copy->mBounds = new float[4];
- memcpy(copy->mBounds, mBounds, sizeof(float) * 4);
- copy->mColors = new uint32_t[mCount];
- memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
- copy->mPositions = new float[mCount];
- memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
- copy->mCount = mCount;
- copy->mIsSimple = mIsSimple;
- return copy;
-}
-
-void SkiaLinearGradientShader::describe(ProgramDescription& description,
- const Extensions& extensions) {
- description.hasGradient = true;
- description.gradientType = ProgramDescription::kGradientLinear;
- description.isSimpleGradient = mIsSimple;
-}
-
-void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
- const Snapshot&, GLuint* textureUnit) {
- if (CC_UNLIKELY(!mIsSimple)) {
- GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().activeTexture(textureSlot);
-
- Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
-
- // Uniforms
- bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
- glUniform1i(program->getUniform("gradientSampler"), textureSlot);
- } else {
- bindUniformColor(program->getUniform("startColor"), mColors[0]);
- bindUniformColor(program->getUniform("endColor"), mColors[1]);
- }
-
- Caches::getInstance().dither.setupProgram(program, textureUnit);
-
- mat4 screenSpace;
- computeScreenSpaceMatrix(screenSpace, modelView);
- glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
-}
-
///////////////////////////////////////////////////////////////////////////////
// Circular gradient shader
///////////////////////////////////////////////////////////////////////////////
@@ -304,37 +322,6 @@
matrix->postScale(inv, inv);
}
-SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius,
- uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
- SkMatrix* matrix, bool blend):
- SkiaSweepGradientShader(kCircularGradient, colors, positions, count, key,
- tileMode, matrix, blend) {
- SkMatrix unitMatrix;
- toCircularUnitMatrix(x, y, radius, &unitMatrix);
- mUnitMatrix.load(unitMatrix);
-
- updateLocalMatrix(matrix);
-}
-
-SkiaShader* SkiaCircularGradientShader::copy() {
- SkiaCircularGradientShader* copy = new SkiaCircularGradientShader();
- copy->copyFrom(*this);
- copy->mColors = new uint32_t[mCount];
- memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
- copy->mPositions = new float[mCount];
- memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
- copy->mCount = mCount;
- copy->mIsSimple = mIsSimple;
- return copy;
-}
-
-void SkiaCircularGradientShader::describe(ProgramDescription& description,
- const Extensions& extensions) {
- description.hasGradient = true;
- description.gradientType = ProgramDescription::kGradientCircular;
- description.isSimpleGradient = mIsSimple;
-}
-
///////////////////////////////////////////////////////////////////////////////
// Sweep gradient shader
///////////////////////////////////////////////////////////////////////////////
@@ -343,74 +330,103 @@
matrix->setTranslate(-x, -y);
}
-SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors,
- float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend):
- SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode,
- SkShader::kClamp_TileMode, matrix, blend),
- mColors(colors), mPositions(positions), mCount(count) {
- SkMatrix unitMatrix;
- toSweepUnitMatrix(x, y, &unitMatrix);
- mUnitMatrix.load(unitMatrix);
+///////////////////////////////////////////////////////////////////////////////
+// Common gradient code
+///////////////////////////////////////////////////////////////////////////////
- updateLocalMatrix(matrix);
-
- mIsSimple = count == 2;
+static bool isSimpleGradient(const SkShader::GradientInfo& gradInfo) {
+ return gradInfo.fColorCount == 2 && gradInfo.fTileMode == SkShader::kClamp_TileMode;
}
-SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, uint32_t* colors,
- float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
- SkMatrix* matrix, bool blend):
- SkiaShader(type, key, tileMode, tileMode, matrix, blend),
- mColors(colors), mPositions(positions), mCount(count) {
- // protected method, that doesn't setup mUnitMatrix - should be handled by subclass
+void SkiaGradientShader::describe(Caches*, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader) {
+ SkShader::GradientInfo gradInfo;
+ gradInfo.fColorCount = 0;
+ gradInfo.fColors = NULL;
+ gradInfo.fColorOffsets = NULL;
- mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
-}
-
-SkiaSweepGradientShader::~SkiaSweepGradientShader() {
- delete[] mColors;
- delete[] mPositions;
-}
-
-SkiaShader* SkiaSweepGradientShader::copy() {
- SkiaSweepGradientShader* copy = new SkiaSweepGradientShader();
- copy->copyFrom(*this);
- copy->mColors = new uint32_t[mCount];
- memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
- copy->mPositions = new float[mCount];
- memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
- copy->mCount = mCount;
- copy->mIsSimple = mIsSimple;
- return copy;
-}
-
-void SkiaSweepGradientShader::describe(ProgramDescription& description,
- const Extensions& extensions) {
+ switch (shader.asAGradient(&gradInfo)) {
+ case SkShader::kLinear_GradientType:
+ description.gradientType = ProgramDescription::kGradientLinear;
+ break;
+ case SkShader::kRadial_GradientType:
+ description.gradientType = ProgramDescription::kGradientCircular;
+ break;
+ case SkShader::kSweep_GradientType:
+ description.gradientType = ProgramDescription::kGradientSweep;
+ break;
+ default:
+ // Do nothing. This shader is unsupported.
+ return;
+ }
description.hasGradient = true;
- description.gradientType = ProgramDescription::kGradientSweep;
- description.isSimpleGradient = mIsSimple;
+ description.isSimpleGradient = isSimpleGradient(gradInfo);
}
-void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView,
- const Snapshot& snapshot, GLuint* textureUnit) {
- if (CC_UNLIKELY(!mIsSimple)) {
- GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().activeTexture(textureSlot);
+void SkiaGradientShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions&, const SkShader& shader) {
+ // SkShader::GradientInfo.fColorCount is an in/out parameter. As input, it tells asAGradient
+ // how much space has been allocated for fColors and fColorOffsets. 10 was chosen
+ // arbitrarily, but should be >= 2.
+ // As output, it tells the number of actual colors/offsets in the gradient.
+ const int COLOR_COUNT = 10;
+ SkAutoSTMalloc<COLOR_COUNT, SkColor> colorStorage(COLOR_COUNT);
+ SkAutoSTMalloc<COLOR_COUNT, SkScalar> positionStorage(COLOR_COUNT);
- Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
+ SkShader::GradientInfo gradInfo;
+ gradInfo.fColorCount = COLOR_COUNT;
+ gradInfo.fColors = colorStorage.get();
+ gradInfo.fColorOffsets = positionStorage.get();
+
+ SkShader::GradientType gradType = shader.asAGradient(&gradInfo);
+
+ Program* program = caches->currentProgram;
+ if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) {
+ if (gradInfo.fColorCount > COLOR_COUNT) {
+ // There was not enough room in our arrays for all the colors and offsets. Try again,
+ // now that we know the true number of colors.
+ gradInfo.fColors = colorStorage.reset(gradInfo.fColorCount);
+ gradInfo.fColorOffsets = positionStorage.reset(gradInfo.fColorCount);
+
+ shader.asAGradient(&gradInfo);
+ }
+ GLuint textureSlot = (*textureUnit)++;
+ caches->activeTexture(textureSlot);
+
+#ifndef SK_SCALAR_IS_FLOAT
+ #error Need to convert gradInfo.fColorOffsets to float!
+#endif
+ Texture* texture = caches->gradientCache.get(gradInfo.fColors, gradInfo.fColorOffsets,
+ gradInfo.fColorCount);
// Uniforms
- bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
+ bindTexture(caches, texture, gTileModes[gradInfo.fTileMode], gTileModes[gradInfo.fTileMode]);
glUniform1i(program->getUniform("gradientSampler"), textureSlot);
} else {
- bindUniformColor(program->getUniform("startColor"), mColors[0]);
- bindUniformColor(program->getUniform("endColor"), mColors[1]);
+ bindUniformColor(program->getUniform("startColor"), gradInfo.fColors[0]);
+ bindUniformColor(program->getUniform("endColor"), gradInfo.fColors[1]);
}
- mCaches->dither.setupProgram(program, textureUnit);
+ caches->dither.setupProgram(program, textureUnit);
+
+ SkMatrix unitMatrix;
+ switch (gradType) {
+ case SkShader::kLinear_GradientType:
+ toUnitMatrix(gradInfo.fPoint, &unitMatrix);
+ break;
+ case SkShader::kRadial_GradientType:
+ toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY,
+ gradInfo.fRadius[0], &unitMatrix);
+ break;
+ case SkShader::kSweep_GradientType:
+ toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Invalid SkShader gradient type %d", gradType);
+ }
mat4 screenSpace;
- computeScreenSpaceMatrix(screenSpace, modelView);
+ computeScreenSpaceMatrix(screenSpace, unitMatrix, shader.getLocalMatrix(), modelViewMatrix);
glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
}
@@ -418,49 +434,39 @@
// Compose shader
///////////////////////////////////////////////////////////////////////////////
-SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second,
- SkXfermode::Mode mode, SkShader* key):
- SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
- NULL, first->blend() || second->blend()),
- mFirst(first), mSecond(second), mMode(mode), mCleanup(false) {
-}
-
-SkiaComposeShader::~SkiaComposeShader() {
- if (mCleanup) {
- delete mFirst;
- delete mSecond;
+void SkiaComposeShader::describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader) {
+ SkShader::ComposeRec rec;
+ if (!shader.asACompose(&rec)) {
+ LOG_ALWAYS_FATAL("SkiaComposeShader::describe called on the wrong shader type!");
}
-}
-
-SkiaShader* SkiaComposeShader::copy() {
- SkiaComposeShader* copy = new SkiaComposeShader();
- copy->copyFrom(*this);
- copy->mFirst = mFirst->copy();
- copy->mSecond = mSecond->copy();
- copy->mMode = mMode;
- copy->cleanup();
- return copy;
-}
-
-void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
- mFirst->describe(description, extensions);
- mSecond->describe(description, extensions);
- if (mFirst->type() == kBitmap) {
+ SkiaShader::describe(caches, description, extensions, *rec.fShaderA);
+ SkiaShader::describe(caches, description, extensions, *rec.fShaderB);
+ if (SkiaShader::getType(*rec.fShaderA) == kBitmap_SkiaShaderType) {
description.isBitmapFirst = true;
}
- description.shadersMode = mMode;
+ if (!SkXfermode::AsMode(rec.fMode, &description.shadersMode)) {
+ // TODO: Support other modes.
+ description.shadersMode = SkXfermode::kSrcOver_Mode;
+ }
}
-void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView,
- const Snapshot& snapshot, GLuint* textureUnit) {
+void SkiaComposeShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+ SkShader::ComposeRec rec;
+ if (!shader.asACompose(&rec)) {
+ LOG_ALWAYS_FATAL("SkiaComposeShader::setupProgram called on the wrong shader type!");
+ }
+
// Apply this compose shader's local transform and pass it down to
// the child shaders. They will in turn apply their local transform
// to this matrix.
mat4 transform;
- computeScreenSpaceMatrix(transform, modelView);
+ computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(),
+ modelViewMatrix);
- mFirst->setupProgram(program, transform, snapshot, textureUnit);
- mSecond->setupProgram(program, transform, snapshot, textureUnit);
+ SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderA);
+ SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderB);
}
}; // namespace uirenderer
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index 9f30257..034c3f6 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -28,249 +28,90 @@
#include "ProgramCache.h"
#include "TextureCache.h"
#include "GradientCache.h"
-#include "Snapshot.h"
namespace android {
namespace uirenderer {
class Caches;
-
-///////////////////////////////////////////////////////////////////////////////
-// Base shader
-///////////////////////////////////////////////////////////////////////////////
+class Layer;
/**
- * Represents a Skia shader. A shader will modify the GL context and active
- * program to recreate the original effect.
+ * Type of Skia shader in use.
*/
+enum SkiaShaderType {
+ kNone_SkiaShaderType,
+ kBitmap_SkiaShaderType,
+ kGradient_SkiaShaderType,
+ kCompose_SkiaShaderType,
+ kLayer_SkiaShaderType
+};
+
class SkiaShader {
public:
- /**
- * Type of Skia shader in use.
- */
- enum Type {
- kNone,
- kBitmap,
- kLinearGradient,
- kCircularGradient,
- kSweepGradient,
- kCompose
- };
+ static SkiaShaderType getType(const SkShader& shader);
+ static void describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader);
+ static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+};
- ANDROID_API SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
- SkShader::TileMode tileY, const SkMatrix* matrix, bool blend);
- virtual ~SkiaShader();
-
- virtual SkiaShader* copy() = 0;
- void copyFrom(const SkiaShader& shader);
-
- virtual void describe(ProgramDescription& description, const Extensions& extensions);
- virtual void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
- GLuint* textureUnit);
-
- inline SkShader* getSkShader() {
- return mKey;
+class InvalidSkiaShader {
+public:
+ static void describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader) {
+ // This shader is unsupported. Skip it.
+ }
+ static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+ // This shader is unsupported. Skip it.
}
- inline bool blend() const {
- return mBlend;
- }
-
- Type type() const {
- return mType;
- }
-
- virtual void setCaches(Caches& caches) {
- mCaches = &caches;
- }
-
- uint32_t getGenerationId() {
- return mGenerationId;
- }
-
- void setMatrix(const SkMatrix* matrix) {
- updateLocalMatrix(matrix);
- mGenerationId++;
- }
-
- void updateLocalMatrix(const SkMatrix* matrix) {
- if (matrix) {
- mat4 localMatrix(*matrix);
- mShaderMatrix.loadInverse(localMatrix);
- } else {
- mShaderMatrix.loadIdentity();
- }
- }
-
- void computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView);
-
-protected:
- SkiaShader();
-
- /**
- * The appropriate texture unit must have been activated prior to invoking
- * this method.
- */
- inline void bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT);
-
- Type mType;
- SkShader* mKey;
- SkShader::TileMode mTileX;
- SkShader::TileMode mTileY;
- bool mBlend;
-
- Caches* mCaches;
-
- mat4 mUnitMatrix;
- mat4 mShaderMatrix;
-
-private:
- uint32_t mGenerationId;
-}; // struct SkiaShader
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Implementations
-///////////////////////////////////////////////////////////////////////////////
-
+};
/**
* A shader that draws a layer.
*/
-struct SkiaLayerShader: public SkiaShader {
- SkiaLayerShader(Layer* layer, const SkMatrix* matrix);
- SkiaShader* copy();
-
- void describe(ProgramDescription& description, const Extensions& extensions);
- void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
- GLuint* textureUnit);
-
-private:
- SkiaLayerShader() {
- }
-
- Layer* mLayer;
-}; // struct SkiaLayerShader
+class SkiaLayerShader {
+public:
+ static void describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader);
+ static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+}; // class SkiaLayerShader
/**
* A shader that draws a bitmap.
*/
-struct SkiaBitmapShader: public SkiaShader {
- ANDROID_API SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
- SkShader::TileMode tileY, SkMatrix* matrix, bool blend);
- SkiaShader* copy();
+class SkiaBitmapShader {
+public:
+ static void describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader);
+ static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
- void describe(ProgramDescription& description, const Extensions& extensions);
- void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
- GLuint* textureUnit);
-private:
- SkiaBitmapShader() : mBitmap(NULL), mTexture(NULL) {
- }
-
- SkBitmap* mBitmap;
- Texture* mTexture;
- GLenum mWrapS;
- GLenum mWrapT;
-}; // struct SkiaBitmapShader
+}; // class SkiaBitmapShader
/**
- * A shader that draws a linear gradient.
+ * A shader that draws one of three types of gradient, depending on shader param.
*/
-struct SkiaLinearGradientShader: public SkiaShader {
- ANDROID_API SkiaLinearGradientShader(float* bounds, uint32_t* colors, float* positions,
- int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
- ~SkiaLinearGradientShader();
- SkiaShader* copy();
-
- void describe(ProgramDescription& description, const Extensions& extensions);
- void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
- GLuint* textureUnit);
-
-private:
- SkiaLinearGradientShader() {
- }
-
- bool mIsSimple;
- float* mBounds;
- uint32_t* mColors;
- float* mPositions;
- int mCount;
-}; // struct SkiaLinearGradientShader
-
-/**
- * A shader that draws a sweep gradient.
- */
-struct SkiaSweepGradientShader: public SkiaShader {
- ANDROID_API SkiaSweepGradientShader(float x, float y, uint32_t* colors, float* positions,
- int count, SkShader* key, SkMatrix* matrix, bool blend);
- ~SkiaSweepGradientShader();
- SkiaShader* copy();
-
- virtual void describe(ProgramDescription& description, const Extensions& extensions);
- void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
- GLuint* textureUnit);
-
-protected:
- SkiaSweepGradientShader(Type type, uint32_t* colors, float* positions,
- int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
- SkiaSweepGradientShader() {
- }
-
- bool mIsSimple;
- uint32_t* mColors;
- float* mPositions;
- int mCount;
-}; // struct SkiaSweepGradientShader
-
-/**
- * A shader that draws a circular gradient.
- */
-struct SkiaCircularGradientShader: public SkiaSweepGradientShader {
- ANDROID_API SkiaCircularGradientShader(float x, float y, float radius, uint32_t* colors,
- float* positions, int count, SkShader* key,SkShader::TileMode tileMode,
- SkMatrix* matrix, bool blend);
- SkiaShader* copy();
-
- void describe(ProgramDescription& description, const Extensions& extensions);
-
-private:
- SkiaCircularGradientShader() {
- }
-}; // struct SkiaCircularGradientShader
+class SkiaGradientShader {
+public:
+ static void describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader);
+ static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+};
/**
* A shader that draws two shaders, composited with an xfermode.
*/
-struct SkiaComposeShader: public SkiaShader {
- ANDROID_API SkiaComposeShader(SkiaShader* first, SkiaShader* second, SkXfermode::Mode mode,
- SkShader* key);
- ~SkiaComposeShader();
- SkiaShader* copy();
-
- void setCaches(Caches& caches) {
- SkiaShader::setCaches(caches);
- mFirst->setCaches(caches);
- mSecond->setCaches(caches);
- }
-
- void describe(ProgramDescription& description, const Extensions& extensions);
- void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
- GLuint* textureUnit);
-
-private:
- SkiaComposeShader(): mCleanup(false) {
- }
-
- void cleanup() {
- mCleanup = true;
- }
-
- SkiaShader* mFirst;
- SkiaShader* mSecond;
- SkXfermode::Mode mMode;
-
- bool mCleanup;
-}; // struct SkiaComposeShader
+class SkiaComposeShader {
+public:
+ static void describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader);
+ static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+}; // class SkiaComposeShader
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 34e2265..60b4b96 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -34,7 +34,7 @@
///////////////////////////////////////////////////////////////////////////////
TextureCache::TextureCache():
- mCache(LruCache<const SkBitmap*, Texture*>::kUnlimitedCapacity),
+ mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity),
mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) {
char property[PROPERTY_VALUE_MAX];
@@ -58,7 +58,7 @@
}
TextureCache::TextureCache(uint32_t maxByteSize):
- mCache(LruCache<const SkBitmap*, Texture*>::kUnlimitedCapacity),
+ mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity),
mSize(0), mMaxSize(maxByteSize) {
init();
}
@@ -103,7 +103,7 @@
// Callbacks
///////////////////////////////////////////////////////////////////////////////
-void TextureCache::operator()(const SkBitmap*&, Texture*& texture) {
+void TextureCache::operator()(const SkPixelRef*&, Texture*& texture) {
// This will be called already locked
if (texture) {
mSize -= texture->bitmapSize;
@@ -122,7 +122,7 @@
///////////////////////////////////////////////////////////////////////////////
void TextureCache::resetMarkInUse() {
- LruCache<const SkBitmap*, Texture*>::Iterator iter(mCache);
+ LruCache<const SkPixelRef*, Texture*>::Iterator iter(mCache);
while (iter.next()) {
iter.value()->isInUse = false;
}
@@ -140,7 +140,7 @@
// Returns a prepared Texture* that either is already in the cache or can fit
// in the cache (and is thus added to the cache)
Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) {
- Texture* texture = mCache.get(bitmap);
+ Texture* texture = mCache.get(bitmap->pixelRef());
if (!texture) {
if (!canMakeTextureFromBitmap(bitmap)) {
@@ -170,7 +170,7 @@
if (mDebugEnabled) {
ALOGD("Texture created, size = %d", size);
}
- mCache.put(bitmap, texture);
+ mCache.put(bitmap->pixelRef(), texture);
}
} else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
// Texture was in the cache but is dirty, re-upload
@@ -218,7 +218,7 @@
}
void TextureCache::remove(const SkBitmap* bitmap) {
- mCache.remove(bitmap);
+ mCache.remove(bitmap->pixelRef());
}
void TextureCache::removeDeferred(const SkBitmap* bitmap) {
@@ -231,7 +231,7 @@
size_t count = mGarbage.size();
for (size_t i = 0; i < count; i++) {
const SkBitmap* bitmap = mGarbage.itemAt(i);
- mCache.remove(bitmap);
+ mCache.remove(bitmap->pixelRef());
delete bitmap;
}
mGarbage.clear();
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 48a10c2..e5b5c1a 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -49,7 +49,7 @@
* Any texture added to the cache causing the cache to grow beyond the maximum
* allowed size will also cause the oldest texture to be kicked out.
*/
-class TextureCache: public OnEntryRemoved<const SkBitmap*, Texture*> {
+class TextureCache: public OnEntryRemoved<const SkPixelRef*, Texture*> {
public:
TextureCache();
TextureCache(uint32_t maxByteSize);
@@ -59,7 +59,7 @@
* Used as a callback when an entry is removed from the cache.
* Do not invoke directly.
*/
- void operator()(const SkBitmap*& bitmap, Texture*& texture);
+ void operator()(const SkPixelRef*& pixelRef, Texture*& texture);
/**
* Resets all Textures to not be marked as in use
@@ -147,7 +147,7 @@
void init();
- LruCache<const SkBitmap*, Texture*> mCache;
+ LruCache<const SkPixelRef*, Texture*> mCache;
uint32_t mSize;
uint32_t mMaxSize;
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index d4a23b8..8355f83 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -52,6 +52,7 @@
: hasFunctors(false)
, hasAnimations(false)
, requiresUiRedraw(false)
+ , canDrawThisFrame(true)
{}
bool hasFunctors;
// This is only updated if evaluateAnimations is true
@@ -60,6 +61,13 @@
// animate itself, such as if hasFunctors is true
// This is only set if hasAnimations is true
bool requiresUiRedraw;
+ // This is set to true if draw() can be called this frame
+ // false means that we must delay until the next vsync pulse as frame
+ // production is outrunning consumption
+ // NOTE that if this is false CanvasContext will set either requiresUiRedraw
+ // *OR* will post itself for the next vsync automatically, use this
+ // only to avoid calling draw()
+ bool canDrawThisFrame;
} out;
// TODO: Damage calculations
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 839ef91..b6b3428 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -360,7 +360,9 @@
setSurface(NULL);
}
-void CanvasContext::setSurface(EGLNativeWindowType window) {
+void CanvasContext::setSurface(ANativeWindow* window) {
+ mNativeWindow = window;
+
if (mEglSurface != EGL_NO_SURFACE) {
mGlobalContext->destroySurface(mEglSurface);
mEglSurface = EGL_NO_SURFACE;
@@ -393,7 +395,7 @@
makeCurrent();
}
-bool CanvasContext::initialize(EGLNativeWindowType window) {
+bool CanvasContext::initialize(ANativeWindow* window) {
if (mCanvas) return false;
setSurface(window);
mCanvas = new OpenGLRenderer();
@@ -401,11 +403,11 @@
return true;
}
-void CanvasContext::updateSurface(EGLNativeWindowType window) {
+void CanvasContext::updateSurface(ANativeWindow* window) {
setSurface(window);
}
-void CanvasContext::pauseSurface(EGLNativeWindowType window) {
+void CanvasContext::pauseSurface(ANativeWindow* window) {
// TODO: For now we just need a fence, in the future suspend any animations
// and such to prevent from trying to render into this surface
}
@@ -456,7 +458,15 @@
info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
mRootRenderNode->prepareTree(info);
- if (info.out.hasAnimations) {
+ int runningBehind = 0;
+ // TODO: This query is moderately expensive, investigate adding some sort
+ // of fast-path based off when we last called eglSwapBuffers() as well as
+ // last vsync time. Or something.
+ mNativeWindow->query(mNativeWindow.get(),
+ NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
+ info.out.canDrawThisFrame = !runningBehind;
+
+ if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
if (info.out.hasFunctors) {
info.out.requiresUiRedraw = true;
} else if (!info.out.requiresUiRedraw) {
@@ -467,6 +477,11 @@
}
}
+void CanvasContext::notifyFramePending() {
+ ATRACE_CALL();
+ mRenderThread.pushBackFrameCallback(this);
+}
+
void CanvasContext::draw(Rect* dirty) {
LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
"drawDisplayList called on a context with no canvas or surface!");
@@ -515,7 +530,9 @@
info.prepareTextures = false;
prepareTree(info);
- draw(NULL);
+ if (info.out.canDrawThisFrame) {
+ draw(NULL);
+ }
}
void CanvasContext::invokeFunctor(Functor* functor) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 4232f27..a54b33e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -48,9 +48,9 @@
CanvasContext(bool translucent, RenderNode* rootRenderNode);
virtual ~CanvasContext();
- bool initialize(EGLNativeWindowType window);
- void updateSurface(EGLNativeWindowType window);
- void pauseSurface(EGLNativeWindowType window);
+ bool initialize(ANativeWindow* window);
+ void updateSurface(ANativeWindow* window);
+ void pauseSurface(ANativeWindow* window);
void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
void setOpaque(bool opaque);
void makeCurrent();
@@ -73,11 +73,15 @@
ANDROID_API static void setTextureAtlas(const sp<GraphicBuffer>& buffer,
int64_t* map, size_t mapSize);
+ void notifyFramePending();
+
private:
+ friend class RegisterFrameCallbackTask;
+
void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
void prepareTree(TreeInfo& info);
- void setSurface(EGLNativeWindowType window);
+ void setSurface(ANativeWindow* window);
void swapBuffers();
void requireSurface();
@@ -85,6 +89,7 @@
GlobalContext* mGlobalContext;
RenderThread& mRenderThread;
+ sp<ANativeWindow> mNativeWindow;
EGLSurface mEglSurface;
bool mDirtyRegionsEnabled;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 3b8786c..ee3e059 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -87,7 +87,13 @@
void DrawFrameTask::run() {
ATRACE_NAME("DrawFrame");
- bool canUnblockUiThread = syncFrameState();
+ bool canUnblockUiThread;
+ bool canDrawThisFrame;
+ {
+ TreeInfo info;
+ canUnblockUiThread = syncFrameState(info);
+ canDrawThisFrame = info.out.canDrawThisFrame;
+ }
// Grab a copy of everything we need
Rect dirty(mDirty);
@@ -98,7 +104,9 @@
unblockUiThread();
}
- context->draw(&dirty);
+ if (CC_LIKELY(canDrawThisFrame)) {
+ context->draw(&dirty);
+ }
if (!canUnblockUiThread) {
unblockUiThread();
@@ -111,12 +119,11 @@
info.evaluateAnimations = true;
}
-bool DrawFrameTask::syncFrameState() {
+bool DrawFrameTask::syncFrameState(TreeInfo& info) {
ATRACE_CALL();
mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos);
mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse();
- TreeInfo info;
initTreeInfo(info);
mContext->prepareDraw(&mLayers, info);
if (info.out.hasAnimations) {
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index b9307e1..acbc02a 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -24,6 +24,7 @@
#include "RenderTask.h"
#include "../Rect.h"
+#include "../TreeInfo.h"
namespace android {
namespace uirenderer {
@@ -65,7 +66,7 @@
private:
void postAndWait();
- bool syncFrameState();
+ bool syncFrameState(TreeInfo& info);
void unblockUiThread();
Mutex mLock;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ef8e45b..2e103d8 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -112,7 +112,7 @@
return (bool) postAndWait(task);
}
-CREATE_BRIDGE2(initialize, CanvasContext* context, EGLNativeWindowType window) {
+CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) {
return (void*) args->context->initialize(args->window);
}
@@ -123,7 +123,7 @@
return (bool) postAndWait(task);
}
-CREATE_BRIDGE2(updateSurface, CanvasContext* context, EGLNativeWindowType window) {
+CREATE_BRIDGE2(updateSurface, CanvasContext* context, ANativeWindow* window) {
args->context->updateSurface(args->window);
return NULL;
}
@@ -135,7 +135,7 @@
postAndWait(task);
}
-CREATE_BRIDGE2(pauseSurface, CanvasContext* context, EGLNativeWindowType window) {
+CREATE_BRIDGE2(pauseSurface, CanvasContext* context, ANativeWindow* window) {
args->context->pauseSurface(args->window);
return NULL;
}
@@ -292,6 +292,17 @@
postAndWait(task);
}
+CREATE_BRIDGE1(notifyFramePending, CanvasContext* context) {
+ args->context->notifyFramePending();
+ return NULL;
+}
+
+void RenderProxy::notifyFramePending() {
+ SETUP_TASK(notifyFramePending);
+ args->context = mContext;
+ mRenderThread.queueAtFront(task);
+}
+
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 4d3499e..8aeb264 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -82,6 +82,7 @@
ANDROID_API void destroyLayer(DeferredLayerUpdater* layer);
ANDROID_API void fence();
+ ANDROID_API void notifyFramePending();
private:
RenderThread& mRenderThread;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 35a3eab..4a4e254 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -37,7 +37,7 @@
static const size_t EVENT_BUFFER_SIZE = 100;
// Slight delay to give the UI time to push us a new frame before we replay
-static const int DISPATCH_FRAME_CALLBACKS_DELAY = 0;
+static const int DISPATCH_FRAME_CALLBACKS_DELAY = 4;
TaskQueue::TaskQueue() : mHead(0), mTail(0) {}
@@ -91,6 +91,15 @@
}
}
+void TaskQueue::queueAtFront(RenderTask* task) {
+ if (mTail) {
+ task->mNext = mHead;
+ mHead = task;
+ } else {
+ mTail = mHead = task;
+ }
+}
+
void TaskQueue::remove(RenderTask* task) {
// TaskQueue is strict here to enforce that users are keeping track of
// their RenderTasks due to how their memory is managed
@@ -188,20 +197,22 @@
return latest;
}
-void RenderThread::drainDisplayEventQueue() {
+void RenderThread::drainDisplayEventQueue(bool skipCallbacks) {
+ ATRACE_CALL();
nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
if (vsyncEvent > 0) {
mVsyncRequested = false;
mTimeLord.vsyncReceived(vsyncEvent);
- if (!mFrameCallbackTaskPending) {
+ if (!skipCallbacks && !mFrameCallbackTaskPending) {
+ ATRACE_NAME("queue mFrameCallbackTask");
mFrameCallbackTaskPending = true;
- //queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
- queue(mFrameCallbackTask);
+ queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
}
}
}
void RenderThread::dispatchFrameCallbacks() {
+ ATRACE_CALL();
mFrameCallbackTaskPending = false;
std::set<IFrameCallback*> callbacks;
@@ -212,6 +223,15 @@
}
}
+void RenderThread::requestVsync() {
+ if (!mVsyncRequested) {
+ mVsyncRequested = true;
+ status_t status = mDisplayEventReceiver->requestNextVsync();
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "requestNextVsync failed with status: %d", status);
+ }
+}
+
bool RenderThread::threadLoop() {
initializeDisplayEventReceiver();
@@ -236,6 +256,14 @@
timeoutMillis = 0;
}
}
+
+ if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
+ drainDisplayEventQueue(true);
+ mFrameCallbacks.insert(
+ mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end());
+ mPendingRegistrationFrameCallbacks.clear();
+ requestVsync();
+ }
}
return false;
@@ -250,6 +278,12 @@
}
}
+void RenderThread::queueAtFront(RenderTask* task) {
+ AutoMutex _lock(mLock);
+ mQueue.queueAtFront(task);
+ mLooper->wake();
+}
+
void RenderThread::queueDelayed(RenderTask* task, int delayMs) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
task->mRunAt = now + milliseconds_to_nanoseconds(delayMs);
@@ -262,17 +296,18 @@
}
void RenderThread::postFrameCallback(IFrameCallback* callback) {
- mFrameCallbacks.insert(callback);
- if (!mVsyncRequested) {
- mVsyncRequested = true;
- status_t status = mDisplayEventReceiver->requestNextVsync();
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "requestNextVsync failed with status: %d", status);
- }
+ mPendingRegistrationFrameCallbacks.insert(callback);
}
void RenderThread::removeFrameCallback(IFrameCallback* callback) {
mFrameCallbacks.erase(callback);
+ mPendingRegistrationFrameCallbacks.erase(callback);
+}
+
+void RenderThread::pushBackFrameCallback(IFrameCallback* callback) {
+ if (mFrameCallbacks.erase(callback)) {
+ mPendingRegistrationFrameCallbacks.insert(callback);
+ }
}
RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
@@ -281,11 +316,13 @@
if (!next) {
mNextWakeup = LLONG_MAX;
} else {
+ mNextWakeup = next->mRunAt;
// Most tasks won't be delayed, so avoid unnecessary systemTime() calls
if (next->mRunAt <= 0 || next->mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) {
next = mQueue.next();
+ } else {
+ next = 0;
}
- mNextWakeup = next->mRunAt;
}
if (nextWakeup) {
*nextWakeup = mNextWakeup;
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 215d294..4412584 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -44,6 +44,7 @@
RenderTask* next();
void queue(RenderTask* task);
+ void queueAtFront(RenderTask* task);
RenderTask* peek();
void remove(RenderTask* task);
@@ -66,12 +67,16 @@
// RenderThread takes complete ownership of tasks that are queued
// and will delete them after they are run
ANDROID_API void queue(RenderTask* task);
+ ANDROID_API void queueAtFront(RenderTask* task);
void queueDelayed(RenderTask* task, int delayMs);
void remove(RenderTask* task);
// Mimics android.view.Choreographer
void postFrameCallback(IFrameCallback* callback);
void removeFrameCallback(IFrameCallback* callback);
+ // If the callback is currently registered, it will be pushed back until
+ // the next vsync. If it is not currently registered this does nothing.
+ void pushBackFrameCallback(IFrameCallback* callback);
TimeLord& timeLord() { return mTimeLord; }
@@ -87,8 +92,9 @@
void initializeDisplayEventReceiver();
static int displayEventReceiverCallback(int fd, int events, void* data);
- void drainDisplayEventQueue();
+ void drainDisplayEventQueue(bool skipCallbacks = false);
void dispatchFrameCallbacks();
+ void requestVsync();
// 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
@@ -104,6 +110,11 @@
DisplayEventReceiver* mDisplayEventReceiver;
bool mVsyncRequested;
std::set<IFrameCallback*> mFrameCallbacks;
+ // We defer the actual registration of these callbacks until
+ // both mQueue *and* mDisplayEventReceiver have been drained off all
+ // immediate events. This makes sure that we catch the next vsync, not
+ // the previous one
+ std::set<IFrameCallback*> mPendingRegistrationFrameCallbacks;
bool mFrameCallbackTaskPending;
DispatchFrameCallbacks* mFrameCallbackTask;
diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp
index c020b40..877a422 100644
--- a/libs/hwui/utils/Blur.cpp
+++ b/libs/hwui/utils/Blur.cpp
@@ -19,6 +19,7 @@
#include <math.h>
#include "Blur.h"
+#include "MathUtils.h"
namespace android {
namespace uirenderer {
@@ -35,6 +36,17 @@
return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f;
}
+// if the original radius was on an integer boundary and the resulting radius
+// is within the conversion error tolerance then we attempt to snap to the
+// original integer boundary.
+uint32_t Blur::convertRadiusToInt(float radius) {
+ const float radiusCeil = ceilf(radius);
+ if (MathUtils::areEqual(radiusCeil, radius)) {
+ return radiusCeil;
+ }
+ return radius;
+}
+
/**
* HWUI has used a slightly different equation than Skia to generate the value
* for sigma and to preserve compatibility we have kept that logic.
diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h
index 79aff65..b145333 100644
--- a/libs/hwui/utils/Blur.h
+++ b/libs/hwui/utils/Blur.h
@@ -27,8 +27,12 @@
public:
// If radius > 0, return the corresponding sigma, else return 0
ANDROID_API static float convertRadiusToSigma(float radius);
- // If sigma > 0.6, return the corresponding radius, else return 0
+ // If sigma > 0.5, return the corresponding radius, else return 0
ANDROID_API static float convertSigmaToRadius(float sigma);
+ // If the original radius was on an integer boundary then after the sigma to
+ // radius conversion a small rounding error may be introduced. This function
+ // accounts for that error and snaps to the appropriate integer boundary.
+ static uint32_t convertRadiusToInt(float radius);
static void generateGaussianWeights(float* weights, int32_t radius);
static void horizontal(float* weights, int32_t radius, const uint8_t* source,
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
new file mode 100644
index 0000000..c088906
--- /dev/null
+++ b/media/java/android/media/AudioDevicePort.java
@@ -0,0 +1,85 @@
+/*
+ * 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;
+
+/**
+ * The AudioDevicePort is a specialized type of AudioPort
+ * describing an input (e.g microphone) or output device (e.g speaker)
+ * of the system.
+ * An AudioDevicePort is an AudioPort controlled by the audio HAL, almost always a physical
+ * device at the boundary of the audio system.
+ * In addition to base audio port attributes, the device descriptor contains:
+ * - the device type (e.g AudioManager.DEVICE_OUT_SPEAKER)
+ * - the device address (e.g MAC adddress for AD2P sink).
+ * @see AudioPort
+ * @hide
+ */
+
+public class AudioDevicePort extends AudioPort {
+
+ private final int mType;
+ private final String mAddress;
+
+ AudioDevicePort(AudioHandle handle, int[] samplingRates, int[] channelMasks,
+ int[] formats, AudioGain[] gains, int type, String address) {
+ super(handle,
+ (AudioManager.isInputDevice(type) == true) ?
+ AudioPort.ROLE_SOURCE : AudioPort.ROLE_SINK,
+ samplingRates, channelMasks, formats, gains);
+ mType = type;
+ mAddress = address;
+ }
+
+ /**
+ * Get the device type (e.g AudioManager.DEVICE_OUT_SPEAKER)
+ */
+ public int type() {
+ return mType;
+ }
+
+ /**
+ * Get the device address. Address format varies with the device type.
+ * - USB devices ({@link AudioManager#DEVICE_OUT_USB_DEVICE},
+ * {@link AudioManager#DEVICE_IN_USB_DEVICE}) use an address composed of the ALSA card number
+ * and device number: "card=2;device=1"
+ * - Bluetooth devices ({@link AudioManager#DEVICE_OUT_BLUETOOTH_SCO},
+ * {@link AudioManager#DEVICE_OUT_BLUETOOTH_SCO}, {@link AudioManager#DEVICE_OUT_BLUETOOTH_A2DP})
+ * use the MAC address of the bluetooth device in the form "00:11:22:AA:BB:CC" as reported by
+ * {@link BluetoothDevice#getAddress()}.
+ * - Deivces that do not have an address will indicate an empty string "".
+ */
+ public String address() {
+ return mAddress;
+ }
+
+ /**
+ * Build a specific configuration of this audio device port for use by methods
+ * like AudioManager.connectAudioPatch().
+ */
+ public AudioDevicePortConfig buildConfig(int samplingRate, int channelMask, int format,
+ AudioGainConfig gain) {
+ return new AudioDevicePortConfig(this, samplingRate, channelMask, format, gain);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof AudioDevicePort)) {
+ return false;
+ }
+ return super.equals(o);
+ }
+}
diff --git a/media/java/android/media/AudioDevicePortConfig.java b/media/java/android/media/AudioDevicePortConfig.java
new file mode 100644
index 0000000..a381e10
--- /dev/null
+++ b/media/java/android/media/AudioDevicePortConfig.java
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+/**
+ * An AudioDevicePortConfig describes a possible configuration of an output or input device
+ * (speaker, headphone, microphone ...).
+ * It is used to specify a sink or source when creating a connection with
+ * AudioManager.connectAudioPatch().
+ * An AudioDevicePortConfig is obtained from AudioDevicePort.buildConfig().
+ * @hide
+ */
+
+public class AudioDevicePortConfig extends AudioPortConfig {
+ AudioDevicePortConfig(AudioDevicePort devicePort, int samplingRate, int channelMask,
+ int format, AudioGainConfig gain) {
+ super((AudioPort)devicePort, samplingRate, channelMask, format, gain);
+ }
+
+ /**
+ * Returns the audio device port this AudioDevicePortConfig is issued from.
+ */
+ public AudioDevicePort port() {
+ return (AudioDevicePort)mPort;
+ }
+}
+
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 57274ee..b07d2c5 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -156,4 +156,10 @@
}
}
+ /** @removed */
+ public AudioFormat()
+ {
+ throw new UnsupportedOperationException("There is no valid usage of this constructor");
+ }
+
}
diff --git a/media/java/android/media/AudioGain.java b/media/java/android/media/AudioGain.java
new file mode 100644
index 0000000..57709d5
--- /dev/null
+++ b/media/java/android/media/AudioGain.java
@@ -0,0 +1,159 @@
+/*
+ * 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;
+
+/**
+ * The AudioGain describes a gain controller. Gain controllers are exposed by
+ * audio ports when the gain is configurable at this port's input or output.
+ * Gain values are expressed in millibels.
+ * A gain controller has the following attributes:
+ * - mode: defines modes of operation or features
+ * MODE_JOINT: all channel gains are controlled simultaneously
+ * MODE_CHANNELS: each channel gain is controlled individually
+ * MODE_RAMP: ramps can be applied when gain changes
+ * - channel mask: indicates for which channels the gain can be controlled
+ * - min value: minimum gain value in millibel
+ * - max value: maximum gain value in millibel
+ * - default value: gain value after reset in millibel
+ * - step value: granularity of gain control in millibel
+ * - min ramp duration: minimum ramp duration in milliseconds
+ * - max ramp duration: maximum ramp duration in milliseconds
+ *
+ * This object is always created by the framework and read only by applications.
+ * Applications get a list of AudioGainDescriptors from AudioPortDescriptor.gains() and can build a
+ * valid gain configuration from AudioGain.buildConfig()
+ * @hide
+ */
+public class AudioGain {
+
+ /**
+ * Bit of AudioGain.mode() field indicating that
+ * all channel gains are controlled simultaneously
+ */
+ public static final int MODE_JOINT = 1;
+ /**
+ * Bit of AudioGain.mode() field indicating that
+ * each channel gain is controlled individually
+ */
+ public static final int MODE_CHANNELS = 2;
+ /**
+ * Bit of AudioGain.mode() field indicating that
+ * ramps can be applied when gain changes. The type of ramp (linear, log etc...) is
+ * implementation specific.
+ */
+ public static final int MODE_RAMP = 4;
+
+ private final int mIndex;
+ private final int mMode;
+ private final int mChannelMask;
+ private final int mMinValue;
+ private final int mMaxValue;
+ private final int mDefaultValue;
+ private final int mStepValue;
+ private final int mRampDurationMinMs;
+ private final int mRampDurationMaxMs;
+
+ // The channel mask passed to the constructor is as specified in AudioFormat
+ // (e.g. AudioFormat.CHANNEL_OUT_STEREO)
+ AudioGain(int index, int mode, int channelMask,
+ int minValue, int maxValue, int defaultValue, int stepValue,
+ int rampDurationMinMs, int rampDurationMaxMs) {
+ mIndex = index;
+ mMode = mode;
+ mChannelMask = channelMask;
+ mMinValue = minValue;
+ mMaxValue = maxValue;
+ mDefaultValue = defaultValue;
+ mStepValue = stepValue;
+ mRampDurationMinMs = rampDurationMinMs;
+ mRampDurationMaxMs = rampDurationMaxMs;
+ }
+
+ /**
+ * Bit field indicating supported modes of operation
+ */
+ public int mode() {
+ return mMode;
+ }
+
+ /**
+ * Indicates for which channels the gain can be controlled
+ * (e.g. AudioFormat.CHANNEL_OUT_STEREO)
+ */
+ public int channelMask() {
+ return mChannelMask;
+ }
+
+ /**
+ * Minimum gain value in millibel
+ */
+ public int minValue() {
+ return mMinValue;
+ }
+
+ /**
+ * Maximum gain value in millibel
+ */
+ public int maxValue() {
+ return mMaxValue;
+ }
+
+ /**
+ * Default gain value in millibel
+ */
+ public int defaultValue() {
+ return mDefaultValue;
+ }
+
+ /**
+ * Granularity of gain control in millibel
+ */
+ public int stepValue() {
+ return mStepValue;
+ }
+
+ /**
+ * Minimum ramp duration in milliseconds
+ * 0 if MODE_RAMP not set
+ */
+ public int rampDurationMinMs() {
+ return mRampDurationMinMs;
+ }
+
+ /**
+ * Maximum ramp duration in milliseconds
+ * 0 if MODE_RAMP not set
+ */
+ public int rampDurationMaxMs() {
+ return mRampDurationMaxMs;
+ }
+
+ /**
+ * Build a valid gain configuration for this gain controller for use by
+ * AudioPortDescriptor.setGain()
+ * @param mode: desired mode of operation
+ * @param channelMask: channels of which the gain should be modified.
+ * @param values: gain values for each channels.
+ * @param rampDurationMs: ramp duration if mode MODE_RAMP is set.
+ * ignored if MODE_JOINT.
+ */
+ public AudioGainConfig buildConfig(int mode, int channelMask,
+ int[] values, int rampDurationMs) {
+ //TODO: check params here
+ return new AudioGainConfig(mIndex, this, mode, channelMask, values, rampDurationMs);
+ }
+}
diff --git a/media/java/android/media/AudioGainConfig.java b/media/java/android/media/AudioGainConfig.java
new file mode 100644
index 0000000..ea61679
--- /dev/null
+++ b/media/java/android/media/AudioGainConfig.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * The AudioGainConfig is used by APIs setting or getting values on a given gain
+ * controller. It contains a valid configuration (value, channels...) for a gain controller
+ * exposed by an audio port.
+ * @see AudioGain
+ * @see AudioPort
+ * @hide
+ */
+public class AudioGainConfig {
+ AudioGain mGain;
+ private final int mIndex;
+ private final int mMode;
+ private final int mChannelMask;
+ private final int mValues[];
+ private final int mRampDurationMs;
+
+ AudioGainConfig(int index, AudioGain gain, int mode, int channelMask,
+ int[] values, int rampDurationMs) {
+ mIndex = index;
+ mGain = gain;
+ mMode = mode;
+ mChannelMask = channelMask;
+ mValues = values;
+ mRampDurationMs = rampDurationMs;
+ }
+
+ /**
+ * get the index of the parent gain.
+ * frameworks use only.
+ */
+ int index() {
+ return mIndex;
+ }
+
+ /**
+ * Bit field indicating requested modes of operation. See {@link AudioGain#MODE_JOINT},
+ * {@link AudioGain#MODE_CHANNELS}, {@link AudioGain#MODE_RAMP}
+ */
+ public int mode() {
+ return mMode;
+ }
+
+ /**
+ * Indicates for which channels the gain is set.
+ * See {@link AudioFormat#CHANNEL_OUT_STEREO}, {@link AudioFormat#CHANNEL_OUT_MONO} ...
+ */
+ public int channelMask() {
+ return mChannelMask;
+ }
+
+ /**
+ * Gain values for each channel in the order of bits set in
+ * channelMask() from LSB to MSB
+ */
+ public int[] values() {
+ return mValues;
+ }
+
+ /**
+ * Ramp duration in milliseconds. N/A if mode() does not
+ * specify MODE_RAMP.
+ */
+ public int rampDurationMs() {
+ return mRampDurationMs;
+ }
+}
diff --git a/media/java/android/media/AudioHandle.java b/media/java/android/media/AudioHandle.java
new file mode 100644
index 0000000..b58e7a3
--- /dev/null
+++ b/media/java/android/media/AudioHandle.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+/**
+ * The AudioHandle is used by the audio framework implementation to
+ * uniquely identify a particular component of the routing topology
+ * (AudioPort or AudioPatch)
+ * It is not visible or used at the API.
+ */
+class AudioHandle {
+ private final int mId;
+
+ AudioHandle(int id) {
+ mId = id;
+ }
+
+ int id() {
+ return mId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof AudioHandle)) {
+ return false;
+ }
+ AudioHandle ah = (AudioHandle)o;
+ return mId == ah.id();
+ }
+
+ @Override
+ public int hashCode() {
+ return mId;
+ }
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9803161..f4affa0 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -39,6 +39,7 @@
import android.view.KeyEvent;
import java.util.HashMap;
+import java.util.ArrayList;
/**
* AudioManager provides access to volume and ringer mode control.
@@ -2966,4 +2967,151 @@
Log.w(TAG, "Error disabling safe media volume", e);
}
}
+
+ /**
+ * Return codes for listAudioPorts(), createAudioPatch() ...
+ */
+
+ /** @hide
+ */
+ public static final int SUCCESS = AudioSystem.SUCCESS;
+ /** @hide
+ */
+ public static final int ERROR = AudioSystem.ERROR;
+ /** @hide
+ */
+ public static final int ERROR_BAD_VALUE = AudioSystem.BAD_VALUE;
+ /** @hide
+ */
+ public static final int ERROR_INVALID_OPERATION = AudioSystem.INVALID_OPERATION;
+ /** @hide
+ */
+ public static final int ERROR_PERMISSION_DENIED = AudioSystem.PERMISSION_DENIED;
+ /** @hide
+ */
+ public static final int ERROR_NO_INIT = AudioSystem.NO_INIT;
+ /** @hide
+ */
+ public static final int ERROR_DEAD_OBJECT = AudioSystem.DEAD_OBJECT;
+
+ /**
+ * Returns a list of descriptors for all audio ports managed by the audio framework.
+ * Audio ports are nodes in the audio framework or audio hardware that can be configured
+ * or connected and disconnected with createAudioPatch() or releaseAudioPatch().
+ * See AudioPort for a list of attributes of each audio port.
+ * @param ports An AudioPort ArrayList where the list will be returned.
+ * @hide
+ */
+ public int listAudioPorts(ArrayList<AudioPort> ports) {
+ return ERROR_INVALID_OPERATION;
+ }
+
+ /**
+ * Specialized version of listAudioPorts() listing only audio devices (AudioDevicePort)
+ * @see listAudioPorts(ArrayList<AudioPort>)
+ * @hide
+ */
+ public int listAudioDevicePorts(ArrayList<AudioPort> devices) {
+ return ERROR_INVALID_OPERATION;
+ }
+
+ /**
+ * Create a connection between two or more devices. The framework will reject the request if
+ * device types are not compatible or the implementation does not support the requested
+ * configuration.
+ * NOTE: current implementation is limited to one source and one sink per patch.
+ * @param patch AudioPatch array where the newly created patch will be returned.
+ * As input, if patch[0] is not null, the specified patch will be replaced by the
+ * new patch created. This avoids calling releaseAudioPatch() when modifying a
+ * patch and allows the implementation to optimize transitions.
+ * @param sources List of source audio ports. All must be AudioPort.ROLE_SOURCE.
+ * @param sinks List of sink audio ports. All must be AudioPort.ROLE_SINK.
+ *
+ * @return - {@link #SUCCESS} if connection is successful.
+ * - {@link #ERROR_BAD_VALUE} if incompatible device types are passed.
+ * - {@link #ERROR_INVALID_OPERATION} if the requested connection is not supported.
+ * - {@link #ERROR_PERMISSION_DENIED} if the client does not have permission to create
+ * a patch.
+ * - {@link #ERROR_DEAD_OBJECT} if the server process is dead
+ * - {@link #ERROR} if patch cannot be connected for any other reason.
+ *
+ * patch[0] contains the newly created patch
+ * @hide
+ */
+ public int createAudioPatch(AudioPatch[] patch,
+ AudioPortConfig[] sources,
+ AudioPortConfig[] sinks) {
+ return ERROR_INVALID_OPERATION;
+ }
+
+ /**
+ * Releases an existing audio patch connection.
+ * @param patch The audio patch to disconnect.
+ * @return - {@link #SUCCESS} if disconnection is successful.
+ * - {@link #ERROR_BAD_VALUE} if the specified patch does not exist.
+ * - {@link #ERROR_PERMISSION_DENIED} if the client does not have permission to release
+ * a patch.
+ * - {@link #ERROR_DEAD_OBJECT} if the server process is dead
+ * - {@link #ERROR} if patch cannot be released for any other reason.
+ * @hide
+ */
+ public int releaseAudioPatch(AudioPatch patch) {
+ return ERROR_INVALID_OPERATION;
+ }
+
+ /**
+ * List all existing connections between audio ports.
+ * @param patches An AudioPatch array where the list will be returned.
+ * @hide
+ */
+ public int listAudioPatches(ArrayList<AudioPatch> patches) {
+ return ERROR_INVALID_OPERATION;
+ }
+
+ /**
+ * Set the gain on the specified AudioPort. The AudioGainConfig config is build by
+ * AudioGain.buildConfig()
+ * @hide
+ */
+ public int setAudioPortGain(AudioPort port, AudioGainConfig gain) {
+ return ERROR_INVALID_OPERATION;
+ }
+
+ /**
+ * Listener registered by client to be notified upon new audio port connections,
+ * disconnections or attributes update.
+ * @hide
+ */
+ public interface OnAudioPortUpdateListener {
+ /**
+ * Callback method called upon audio port list update.
+ * @param portList the updated list of audio ports
+ */
+ public void OnAudioPortListUpdate(AudioPort[] portList);
+
+ /**
+ * Callback method called upon audio patch list update.
+ * @param patchList the updated list of audio patches
+ */
+ public void OnAudioPatchListUpdate(AudioPatch[] patchList);
+
+ /**
+ * Callback method called when the mediaserver dies
+ */
+ public void OnServiceDied();
+ }
+
+ /**
+ * Register an audio port update listener.
+ * @hide
+ */
+ public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+ }
+
+ /**
+ * Unregister an audio port update listener.
+ * @hide
+ */
+ public void unregisterAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+ }
}
diff --git a/media/java/android/media/AudioMixPort.java b/media/java/android/media/AudioMixPort.java
new file mode 100644
index 0000000..1500a43
--- /dev/null
+++ b/media/java/android/media/AudioMixPort.java
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+/**
+ * The AudioMixPort is a specialized type of AudioPort
+ * describing an audio mix or stream at an input or output stream of the audio
+ * framework.
+ * @see AudioPort
+ * @hide
+ */
+
+public class AudioMixPort extends AudioPort {
+
+ AudioMixPort(AudioHandle handle, int role, int[] samplingRates, int[] channelMasks,
+ int[] formats, AudioGain[] gains) {
+ super(handle, role, samplingRates, channelMasks, formats, gains);
+ }
+
+ /**
+ * Build a specific configuration of this audio mix port for use by methods
+ * like AudioManager.connectAudioPatch().
+ */
+ public AudioMixPortConfig buildConfig(int samplingRate, int channelMask, int format,
+ AudioGainConfig gain) {
+ return new AudioMixPortConfig(this, samplingRate, channelMask, format, gain);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof AudioMixPort)) {
+ return false;
+ }
+ return super.equals(o);
+ }
+
+}
diff --git a/media/java/android/media/AudioMixPortConfig.java b/media/java/android/media/AudioMixPortConfig.java
new file mode 100644
index 0000000..8eb9ef46
--- /dev/null
+++ b/media/java/android/media/AudioMixPortConfig.java
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+/**
+ * An AudioMixPortConfig describes a possible configuration of an output or input mixer.
+ * It is used to specify a sink or source when creating a connection with
+ * AudioManager.connectAudioPatch().
+ * An AudioMixPortConfig is obtained from AudioMixPort.buildConfig().
+ * @hide
+ */
+
+public class AudioMixPortConfig extends AudioPortConfig {
+
+ AudioMixPortConfig(AudioMixPort mixPort, int samplingRate, int channelMask, int format,
+ AudioGainConfig gain) {
+ super((AudioPort)mixPort, samplingRate, channelMask, format, gain);
+ }
+
+ /**
+ * Returns the audio mix port this AudioMixPortConfig is issued from.
+ */
+ public AudioMixPort port() {
+ return (AudioMixPort)mPort;
+ }
+}
+
diff --git a/media/java/android/media/AudioPatch.java b/media/java/android/media/AudioPatch.java
new file mode 100644
index 0000000..72291f6
--- /dev/null
+++ b/media/java/android/media/AudioPatch.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+
+/**
+ * An AudioPatch describes a connection between audio sources and audio sinks.
+ * An audio source can be an output mix (playback AudioBus) or an input device (microphone).
+ * An audio sink can be an output device (speaker) or an input mix (capture AudioBus).
+ * An AudioPatch is created by AudioManager.connectAudioPatch() and released by
+ * AudioManager.disconnectAudioPatch()
+ * It contains the list of source and sink AudioPortConfig showing audio port configurations
+ * being connected.
+ * @hide
+ */
+public class AudioPatch {
+
+ private final AudioHandle mHandle;
+ private final AudioPortConfig[] mSources;
+ private final AudioPortConfig[] mSinks;
+
+ AudioPatch(AudioHandle patchHandle, AudioPortConfig[] sources, AudioPortConfig[] sinks) {
+ mHandle = patchHandle;
+ mSources = sources;
+ mSinks = sinks;
+ }
+
+ /**
+ * Retrieve the list of sources of this audio patch.
+ */
+ public AudioPortConfig[] sources() {
+ return mSources;
+ }
+
+ /**
+ * Retreive the list of sinks of this audio patch.
+ */
+ public AudioPortConfig[] sinks() {
+ return mSinks;
+ }
+}
diff --git a/media/java/android/media/AudioPort.java b/media/java/android/media/AudioPort.java
new file mode 100644
index 0000000..9aeddef
--- /dev/null
+++ b/media/java/android/media/AudioPort.java
@@ -0,0 +1,174 @@
+/*
+ * 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;
+
+
+/**
+ * An audio port is a node of the audio framework or hardware that can be connected to or
+ * disconnect from another audio node to create a specific audio routing configuration.
+ * Examples of audio ports are an output device (speaker) or an output mix (see AudioMixPort).
+ * All attributes that are relevant for applications to make routing selection are decribed
+ * in an AudioPort, in particular:
+ * - possible channel mask configurations.
+ * - audio format (PCM 16bit, PCM 24bit...)
+ * - gain: a port can be associated with one or more gain controllers (see AudioGain).
+ *
+ * This object is always created by the framework and read only by applications.
+ * A list of all audio port descriptors currently available for applications to control
+ * is obtained by AudioManager.listAudioPorts().
+ * An application can obtain an AudioPortConfig for a valid configuration of this port
+ * by calling AudioPort.buildConfig() and use this configuration
+ * to create a connection between audio sinks and sources with AudioManager.connectAudioPatch()
+ *
+ * @hide
+ */
+public class AudioPort {
+
+ /**
+ * For use by the audio framework.
+ */
+ public static final int ROLE_NONE = 0;
+ /**
+ * The audio port is a source (produces audio)
+ */
+ public static final int ROLE_SOURCE = 1;
+ /**
+ * The audio port is a sink (consumes audio)
+ */
+ public static final int ROLE_SINK = 2;
+
+ /**
+ * audio port type for use by audio framework implementation
+ */
+ public static final int TYPE_NONE = 0;
+ /**
+ */
+ public static final int TYPE_DEVICE = 1;
+ /**
+ */
+ public static final int TYPE_SUBMIX = 2;
+ /**
+ */
+ public static final int TYPE_SESSION = 3;
+
+
+ AudioHandle mHandle;
+ private final int mRole;
+ private final int[] mSamplingRates;
+ private final int[] mChannelMasks;
+ private final int[] mFormats;
+ private final AudioGain[] mGains;
+ private AudioPortConfig mActiveConfig;
+
+ AudioPort(AudioHandle handle, int role, int[] samplingRates, int[] channelMasks,
+ int[] formats, AudioGain[] gains) {
+ mHandle = handle;
+ mRole = role;
+ mSamplingRates = samplingRates;
+ mChannelMasks = channelMasks;
+ mFormats = formats;
+ mGains = gains;
+ }
+
+ AudioHandle handle() {
+ return mHandle;
+ }
+
+ /**
+ * Get the audio port role
+ */
+ public int role() {
+ return mRole;
+ }
+
+ /**
+ * Get the list of supported sampling rates
+ * Empty array if sampling rate is not relevant for this audio port
+ */
+ public int[] samplingRates() {
+ return mSamplingRates;
+ }
+
+ /**
+ * Get the list of supported channel mask configurations
+ * (e.g AudioFormat.CHANNEL_OUT_STEREO)
+ * Empty array if channel mask is not relevant for this audio port
+ */
+ public int[] channelMasks() {
+ return mChannelMasks;
+ }
+
+ /**
+ * Get the list of supported audio format configurations
+ * (e.g AudioFormat.ENCODING_PCM_16BIT)
+ * Empty array if format is not relevant for this audio port
+ */
+ public int[] formats() {
+ return mFormats;
+ }
+
+ /**
+ * Get the list of gain descriptors
+ * Empty array if this port does not have gain control
+ */
+ public AudioGain[] gains() {
+ return mGains;
+ }
+
+ /**
+ * Get the gain descriptor at a given index
+ */
+ AudioGain gain(int index) {
+ return mGains[index];
+ }
+
+ /**
+ * Build a specific configuration of this audio port for use by methods
+ * like AudioManager.connectAudioPatch().
+ * @param channelMask The desired channel mask. AudioFormat.CHANNEL_OUT_DEFAULT if no change
+ * from active configuration requested.
+ * @param format The desired audio format. AudioFormat.ENCODING_DEFAULT if no change
+ * from active configuration requested.
+ * @param gain The desired gain. null if no gain changed requested.
+ */
+ public AudioPortConfig buildConfig(int samplingRate, int channelMask, int format,
+ AudioGainConfig gain) {
+ return new AudioPortConfig(this, samplingRate, channelMask, format, gain);
+ }
+
+ /**
+ * Get currently active configuration of this audio port.
+ */
+ public AudioPortConfig activeConfig() {
+ return mActiveConfig;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof AudioPort)) {
+ return false;
+ }
+ AudioPort ap = (AudioPort)o;
+ return mHandle.equals(ap.handle());
+ }
+
+ @Override
+ public int hashCode() {
+ return mHandle.hashCode();
+ }
+}
+
diff --git a/media/java/android/media/AudioPortConfig.java b/media/java/android/media/AudioPortConfig.java
new file mode 100644
index 0000000..5dc768d
--- /dev/null
+++ b/media/java/android/media/AudioPortConfig.java
@@ -0,0 +1,93 @@
+/*
+ * 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;
+
+/**
+ * An AudioPortConfig contains a possible configuration of an audio port chosen
+ * among all possible attributes described by an AudioPort.
+ * An AudioPortConfig is created by AudioPort.buildConfiguration().
+ * AudioPorts are used to specify the sources and sinks of a patch created
+ * with AudioManager.connectAudioPatch().
+ * Several specialized versions of AudioPortConfig exist to handle different categories of
+ * audio ports and their specific attributes:
+ * - AudioDevicePortConfig for input (e.g micropohone) and output devices (e.g speaker)
+ * - AudioMixPortConfig for input or output streams of the audio framework.
+ * @hide
+ */
+
+public class AudioPortConfig {
+ final AudioPort mPort;
+ private final int mSamplingRate;
+ private final int mChannelMask;
+ private final int mFormat;
+ private final AudioGainConfig mGain;
+
+ // mConfigMask indicates which fields in this configuration should be
+ // taken into account. Used with AudioSystem.setAudioPortConfig()
+ // framework use only.
+ static final int SAMPLE_RATE = 0x1;
+ static final int CHANNEL_MASK = 0x2;
+ static final int FORMAT = 0x4;
+ static final int GAIN = 0x8;
+ int mConfigMask;
+
+ AudioPortConfig(AudioPort port, int samplingRate, int channelMask, int format,
+ AudioGainConfig gain) {
+ mPort = port;
+ mSamplingRate = samplingRate;
+ mChannelMask = channelMask;
+ mFormat = format;
+ mGain = gain;
+ mConfigMask = 0;
+ }
+
+ /**
+ * Returns the audio port this AudioPortConfig is issued from.
+ */
+ public AudioPort port() {
+ return mPort;
+ }
+
+ /**
+ * Sampling rate configured for this AudioPortConfig.
+ */
+ public int samplingRate() {
+ return mSamplingRate;
+ }
+
+ /**
+ * Channel mask configuration (e.g AudioFormat.CHANNEL_CONFIGURATION_STEREO).
+ */
+ public int channelMask() {
+ return mChannelMask;
+ }
+
+ /**
+ * Audio format configuration (e.g AudioFormat.ENCODING_PCM_16BIT).
+ */
+ public int format() {
+ return mFormat;
+ }
+
+ /**
+ * The gain configuration if this port supports gain control, null otherwise
+ * @see AudioGainConfig.
+ */
+ public AudioGainConfig gain() {
+ return mGain;
+ }
+}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index c736fc7..cf18902 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -112,6 +112,9 @@
private static final boolean USE_SESSIONS = true;
private static final boolean DEBUG_SESSIONS = true;
+ /** Allow volume changes to set ringer mode to silent? */
+ private static final boolean VOLUME_SETS_RINGER_MODE_SILENT = false;
+
/** How long to delay before persisting a change in volume/ringer mode. */
private static final int PERSIST_DELAY = 500;
@@ -1019,7 +1022,8 @@
int newRingerMode;
if (index == 0) {
newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
- : AudioManager.RINGER_MODE_SILENT;
+ : VOLUME_SETS_RINGER_MODE_SILENT ? AudioManager.RINGER_MODE_SILENT
+ : AudioManager.RINGER_MODE_NORMAL;
} else {
newRingerMode = AudioManager.RINGER_MODE_NORMAL;
}
@@ -2552,7 +2556,9 @@
}
} else {
// (oldIndex < step) is equivalent to (old UI index == 0)
- if ((oldIndex < step) && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
+ if ((oldIndex < step)
+ && VOLUME_SETS_RINGER_MODE_SILENT
+ && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
ringerMode = RINGER_MODE_SILENT;
}
}
@@ -2565,7 +2571,8 @@
break;
}
if ((direction == AudioManager.ADJUST_LOWER)) {
- if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
+ if (VOLUME_SETS_RINGER_MODE_SILENT
+ && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
ringerMode = RINGER_MODE_SILENT;
}
} else if (direction == AudioManager.ADJUST_RAISE) {
@@ -2909,54 +2916,56 @@
return name + "_" + suffix;
}
- public synchronized void readSettings() {
- // force maximum volume on all streams if fixed volume property is set
- if (mUseFixedVolume) {
- mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
- return;
- }
- // do not read system stream volume from settings: this stream is always aliased
- // to another stream type and its volume is never persisted. Values in settings can
- // only be stale values
- if ((mStreamType == AudioSystem.STREAM_SYSTEM) ||
- (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) {
- int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
- synchronized (mCameraSoundForced) {
- if (mCameraSoundForced) {
- index = mIndexMax;
+ public void readSettings() {
+ synchronized (VolumeStreamState.class) {
+ // force maximum volume on all streams if fixed volume property is set
+ if (mUseFixedVolume) {
+ mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
+ return;
+ }
+ // do not read system stream volume from settings: this stream is always aliased
+ // to another stream type and its volume is never persisted. Values in settings can
+ // only be stale values
+ if ((mStreamType == AudioSystem.STREAM_SYSTEM) ||
+ (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) {
+ int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
+ synchronized (mCameraSoundForced) {
+ if (mCameraSoundForced) {
+ index = mIndexMax;
+ }
}
- }
- mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
- return;
- }
-
- int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
-
- for (int i = 0; remainingDevices != 0; i++) {
- int device = (1 << i);
- if ((device & remainingDevices) == 0) {
- continue;
- }
- remainingDevices &= ~device;
-
- // retrieve current volume for device
- String name = getSettingNameForDevice(device);
- // if no volume stored for current stream and device, use default volume if default
- // device, continue otherwise
- int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
- AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
- int index = Settings.System.getIntForUser(
- mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
- if (index == -1) {
- continue;
+ mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
+ return;
}
- // 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));
+ int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
+
+ for (int i = 0; remainingDevices != 0; i++) {
+ int device = (1 << i);
+ if ((device & remainingDevices) == 0) {
+ continue;
+ }
+ remainingDevices &= ~device;
+
+ // retrieve current volume for device
+ String name = getSettingNameForDevice(device);
+ // if no volume stored for current stream and device, use default volume if default
+ // device, continue otherwise
+ int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
+ AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
+ int index = Settings.System.getIntForUser(
+ mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
+ if (index == -1) {
+ 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));
+ }
}
}
}
@@ -2974,32 +2983,34 @@
AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
}
- public synchronized void applyAllVolumes() {
- // apply default volume first: by convention this will reset all
- // devices volumes in audio policy manager to the supplied value
- int index;
- if (isMuted()) {
- index = 0;
- } else {
- index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
- }
- AudioSystem.setStreamVolumeIndex(mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT);
- // then apply device specific volumes
- Set set = mIndex.entrySet();
- Iterator i = set.iterator();
- while (i.hasNext()) {
- Map.Entry entry = (Map.Entry)i.next();
- int device = ((Integer)entry.getKey()).intValue();
- if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
- if (isMuted()) {
- index = 0;
- } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
- mAvrcpAbsVolSupported) {
- index = (mIndexMax + 5)/10;
- } else {
- index = ((Integer)entry.getValue() + 5)/10;
+ public void applyAllVolumes() {
+ synchronized (VolumeStreamState.class) {
+ // apply default volume first: by convention this will reset all
+ // devices volumes in audio policy manager to the supplied value
+ int index;
+ if (isMuted()) {
+ index = 0;
+ } else {
+ index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
+ }
+ AudioSystem.setStreamVolumeIndex(mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT);
+ // then apply device specific volumes
+ Set set = mIndex.entrySet();
+ Iterator i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ int device = ((Integer)entry.getKey()).intValue();
+ if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
+ if (isMuted()) {
+ index = 0;
+ } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
+ mAvrcpAbsVolSupported) {
+ index = (mIndexMax + 5)/10;
+ } else {
+ index = ((Integer)entry.getValue() + 5)/10;
+ }
+ AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
}
- AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
}
}
}
@@ -3009,94 +3020,104 @@
device);
}
- public synchronized boolean setIndex(int index, int device) {
- int oldIndex = getIndex(device);
- index = getValidIndex(index);
- synchronized (mCameraSoundForced) {
- if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
- index = mIndexMax;
- }
- }
- mIndex.put(device, index);
-
- if (oldIndex != index) {
- // Apply change to all streams using this one as alias
- // if changing volume of current device, also change volume of current
- // device on aliased stream
- boolean currentDevice = (device == getDeviceForStream(mStreamType));
- int numStreamTypes = AudioSystem.getNumStreamTypes();
- for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
- if (streamType != mStreamType &&
- mStreamVolumeAlias[streamType] == mStreamType) {
- int scaledIndex = rescaleIndex(index, mStreamType, streamType);
- mStreamStates[streamType].setIndex(scaledIndex,
- device);
- if (currentDevice) {
- mStreamStates[streamType].setIndex(scaledIndex,
- getDeviceForStream(streamType));
- }
+ public boolean setIndex(int index, int device) {
+ synchronized (VolumeStreamState.class) {
+ int oldIndex = getIndex(device);
+ index = getValidIndex(index);
+ synchronized (mCameraSoundForced) {
+ if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
+ index = mIndexMax;
}
}
- return true;
- } else {
- return false;
+ mIndex.put(device, index);
+
+ if (oldIndex != index) {
+ // Apply change to all streams using this one as alias
+ // if changing volume of current device, also change volume of current
+ // device on aliased stream
+ boolean currentDevice = (device == getDeviceForStream(mStreamType));
+ int numStreamTypes = AudioSystem.getNumStreamTypes();
+ for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
+ if (streamType != mStreamType &&
+ mStreamVolumeAlias[streamType] == mStreamType) {
+ int scaledIndex = rescaleIndex(index, mStreamType, streamType);
+ mStreamStates[streamType].setIndex(scaledIndex,
+ device);
+ if (currentDevice) {
+ mStreamStates[streamType].setIndex(scaledIndex,
+ getDeviceForStream(streamType));
+ }
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
}
}
- public synchronized int getIndex(int device) {
- Integer index = mIndex.get(device);
- if (index == null) {
- // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
- index = mIndex.get(AudioSystem.DEVICE_OUT_DEFAULT);
+ public int getIndex(int device) {
+ synchronized (VolumeStreamState.class) {
+ Integer index = mIndex.get(device);
+ if (index == null) {
+ // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
+ index = mIndex.get(AudioSystem.DEVICE_OUT_DEFAULT);
+ }
+ return index.intValue();
}
- return index.intValue();
}
public int getMaxIndex() {
return mIndexMax;
}
- public synchronized void setAllIndexes(VolumeStreamState srcStream) {
- int srcStreamType = srcStream.getStreamType();
- // apply default device volume from source stream to all devices first in case
- // some devices are present in this stream state but not in source stream state
- int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
- index = rescaleIndex(index, srcStreamType, mStreamType);
- Set set = mIndex.entrySet();
- Iterator i = set.iterator();
- while (i.hasNext()) {
- Map.Entry entry = (Map.Entry)i.next();
- entry.setValue(index);
- }
- // Now apply actual volume for devices in source stream state
- set = srcStream.mIndex.entrySet();
- i = set.iterator();
- while (i.hasNext()) {
- Map.Entry entry = (Map.Entry)i.next();
- int device = ((Integer)entry.getKey()).intValue();
- index = ((Integer)entry.getValue()).intValue();
+ public void setAllIndexes(VolumeStreamState srcStream) {
+ synchronized (VolumeStreamState.class) {
+ int srcStreamType = srcStream.getStreamType();
+ // apply default device volume from source stream to all devices first in case
+ // some devices are present in this stream state but not in source stream state
+ int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
index = rescaleIndex(index, srcStreamType, mStreamType);
+ Set set = mIndex.entrySet();
+ Iterator i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ entry.setValue(index);
+ }
+ // Now apply actual volume for devices in source stream state
+ set = srcStream.mIndex.entrySet();
+ i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ int device = ((Integer)entry.getKey()).intValue();
+ index = ((Integer)entry.getValue()).intValue();
+ index = rescaleIndex(index, srcStreamType, mStreamType);
- setIndex(index, device);
+ setIndex(index, device);
+ }
}
}
- public synchronized void setAllIndexesToMax() {
- Set set = mIndex.entrySet();
- Iterator i = set.iterator();
- while (i.hasNext()) {
- Map.Entry entry = (Map.Entry)i.next();
- entry.setValue(mIndexMax);
+ public void setAllIndexesToMax() {
+ synchronized (VolumeStreamState.class) {
+ Set set = mIndex.entrySet();
+ Iterator i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ entry.setValue(mIndexMax);
+ }
}
}
- public synchronized void mute(IBinder cb, boolean state) {
- VolumeDeathHandler handler = getDeathHandler(cb, state);
- if (handler == null) {
- Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
- return;
+ public void mute(IBinder cb, boolean state) {
+ synchronized (VolumeStreamState.class) {
+ VolumeDeathHandler handler = getDeathHandler(cb, state);
+ if (handler == null) {
+ Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
+ return;
+ }
+ handler.mute(state);
}
- handler.mute(state);
}
public int getStreamType() {
diff --git a/media/java/android/media/DngCreator.java b/media/java/android/media/DngCreator.java
deleted file mode 100644
index 76c6d46..0000000
--- a/media/java/android/media/DngCreator.java
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * 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;
-
-import android.graphics.Bitmap;
-import android.graphics.ImageFormat;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.impl.CameraMetadataNative;
-import android.location.Location;
-import android.util.Size;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-
-/**
- * The {@link DngCreator} class provides functions to write raw pixel data as a DNG file.
- *
- * <p>
- * This class is designed to be used with the {@link android.graphics.ImageFormat#RAW_SENSOR}
- * buffers available from {@link android.hardware.camera2.CameraDevice}, or with Bayer-type raw
- * pixel data that is otherwise generated by an application. The DNG metadata tags will be
- * generated from a {@link android.hardware.camera2.CaptureResult} object or set directly.
- * </p>
- *
- * <p>
- * The DNG file format is a cross-platform file format that is used to store pixel data from
- * camera sensors with minimal pre-processing applied. DNG files allow for pixel data to be
- * defined in a user-defined colorspace, and have associated metadata that allow for this
- * pixel data to be converted to the standard CIE XYZ colorspace during post-processing.
- * </p>
- *
- * <p>
- * For more information on the DNG file format and associated metadata, please refer to the
- * <a href=
- * "https://wwwimages2.adobe.com/content/dam/Adobe/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf">
- * Adobe DNG 1.4.0.0 specification</a>.
- * </p>
- */
-public final class DngCreator implements AutoCloseable {
-
- /**
- * Create a new DNG object.
- *
- * <p>
- * It is not necessary to call any set methods to write a well-formatted DNG file.
- * </p>
- * <p>
- * DNG metadata tags will be generated from the corresponding parameters in the
- * {@link android.hardware.camera2.CaptureResult} object. This removes or overrides
- * all previous tags set.
- * </p>
- *
- * @param characteristics an object containing the static
- * {@link android.hardware.camera2.CameraCharacteristics}.
- * @param metadata a metadata object to generate tags from.
- */
- public DngCreator(CameraCharacteristics characteristics, CaptureResult metadata) {
- if (characteristics == null || metadata == null) {
- throw new NullPointerException("Null argument to DngCreator constructor");
- }
- nativeInit(characteristics.getNativeCopy(), metadata.getNativeCopy());
- }
-
- /**
- * Set the orientation value to write.
- *
- * <p>
- * This will be written as the TIFF "Orientation" tag {@code (0x0112)}.
- * Calling this will override any prior settings for this tag.
- * </p>
- *
- * @param orientation the orientation value to set, one of:
- * <ul>
- * <li>{@link android.media.ExifInterface#ORIENTATION_NORMAL}</li>
- * <li>{@link android.media.ExifInterface#ORIENTATION_FLIP_HORIZONTAL}</li>
- * <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_180}</li>
- * <li>{@link android.media.ExifInterface#ORIENTATION_FLIP_VERTICAL}</li>
- * <li>{@link android.media.ExifInterface#ORIENTATION_TRANSPOSE}</li>
- * <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_90}</li>
- * <li>{@link android.media.ExifInterface#ORIENTATION_TRANSVERSE}</li>
- * <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_270}</li>
- * </ul>
- * @return this {@link #DngCreator} object.
- */
- public DngCreator setOrientation(int orientation) {
-
- if (orientation < ExifInterface.ORIENTATION_UNDEFINED ||
- orientation > ExifInterface.ORIENTATION_ROTATE_270) {
- throw new IllegalArgumentException("Orientation " + orientation +
- " is not a valid EXIF orientation value");
- }
- nativeSetOrientation(orientation);
- return this;
- }
-
- /**
- * Set the thumbnail image.
- *
- * <p>
- * Pixel data will be converted to a Baseline TIFF RGB image, with 8 bits per color channel.
- * The alpha channel will be discarded.
- * </p>
- *
- * <p>
- * The given bitmap should not be altered while this object is in use.
- * </p>
- *
- * @param pixels a {@link android.graphics.Bitmap} of pixel data.
- * @return this {@link #DngCreator} object.
- */
- public DngCreator setThumbnail(Bitmap pixels) {
- if (pixels == null) {
- throw new NullPointerException("Null argument to setThumbnail");
- }
-
- Bitmap.Config config = pixels.getConfig();
-
- if (config != Bitmap.Config.ARGB_8888) {
- pixels = pixels.copy(Bitmap.Config.ARGB_8888, false);
- if (pixels == null) {
- throw new IllegalArgumentException("Unsupported Bitmap format " + config);
- }
- nativeSetThumbnailBitmap(pixels);
- }
-
- return this;
- }
-
- /**
- * Set the thumbnail image.
- *
- * <p>
- * Pixel data is interpreted as a {@link android.graphics.ImageFormat#YUV_420_888} image.
- * </p>
- *
- * <p>
- * The given image should not be altered while this object is in use.
- * </p>
- *
- * @param pixels an {@link android.media.Image} object with the format
- * {@link android.graphics.ImageFormat#YUV_420_888}.
- * @return this {@link #DngCreator} object.
- */
- public DngCreator setThumbnail(Image pixels) {
- if (pixels == null) {
- throw new NullPointerException("Null argument to setThumbnail");
- }
-
- int format = pixels.getFormat();
- if (format != ImageFormat.YUV_420_888) {
- throw new IllegalArgumentException("Unsupported image format " + format);
- }
-
- Image.Plane[] planes = pixels.getPlanes();
- nativeSetThumbnailImage(pixels.getWidth(), pixels.getHeight(), planes[0].getBuffer(),
- planes[0].getRowStride(), planes[0].getPixelStride(), planes[1].getBuffer(),
- planes[1].getRowStride(), planes[1].getPixelStride(), planes[1].getBuffer(),
- planes[1].getRowStride(), planes[1].getPixelStride());
-
- return this;
- }
-
-
- /**
- * Set image location metadata.
- *
- * <p>
- * The given location object must contain at least a valid time, latitude, and longitude
- * (equivalent to the values returned by {@link android.location.Location#getTime()},
- * {@link android.location.Location#getLatitude()}, and
- * {@link android.location.Location#getLongitude()} methods).
- * </p>
- *
- * @param location an {@link android.location.Location} object to set.
- * @return this {@link #DngCreator} object.
- *
- * @throws java.lang.IllegalArgumentException if the given location object doesn't
- * contain enough information to set location metadata.
- */
- public DngCreator setLocation(Location location) {
- /*TODO*/
- return this;
- }
-
- /**
- * Set the user description string to write.
- *
- * <p>
- * This is equivalent to setting the TIFF "ImageDescription" tag {@code (0x010E)}.
- * </p>
- *
- * @param description the user description string.
- * @return this {@link #DngCreator} object.
- */
- public DngCreator setDescription(String description) {
- /*TODO*/
- return this;
- }
-
- /**
- * Write the {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data to a DNG file with
- * the currently configured metadata.
- *
- * <p>
- * Raw pixel data must have 16 bits per pixel, and the input must contain at least
- * {@code offset + 2 * width * height)} bytes. The width and height of
- * the input are taken from the width and height set in the {@link DngCreator} metadata tags,
- * and will typically be equal to the width and height of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
- * The pixel layout in the input is determined from the reported color filter arrangement (CFA)
- * set in {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT}. If insufficient
- * metadata is available to write a well-formatted DNG file, an
- * {@link java.lang.IllegalStateException} will be thrown.
- * </p>
- *
- * @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
- * @param size the {@link Size} of the image to write, in pixels.
- * @param pixels an {@link java.io.InputStream} of pixel data to write.
- * @param offset the offset of the raw image in bytes. This indicates how many bytes will
- * be skipped in the input before any pixel data is read.
- *
- * @throws IOException if an error was encountered in the input or output stream.
- * @throws java.lang.IllegalStateException if not enough metadata information has been
- * set to write a well-formatted DNG file.
- * @throws java.lang.IllegalArgumentException if the size passed in does not match the
- */
- public void writeInputStream(OutputStream dngOutput, Size size, InputStream pixels, long offset)
- throws IOException {
- if (dngOutput == null || pixels == null) {
- throw new NullPointerException("Null argument to writeImage");
- }
- nativeWriteInputStream(dngOutput, pixels, offset);
- }
-
- /**
- * Write the {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data to a DNG file with
- * the currently configured metadata.
- *
- * <p>
- * Raw pixel data must have 16 bits per pixel, and the input must contain at least
- * {@code offset + 2 * width * height)} bytes. The width and height of
- * the input are taken from the width and height set in the {@link DngCreator} metadata tags,
- * and will typically be equal to the width and height of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
- * The pixel layout in the input is determined from the reported color filter arrangement (CFA)
- * set in {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT}. If insufficient
- * metadata is available to write a well-formatted DNG file, an
- * {@link java.lang.IllegalStateException} will be thrown.
- * </p>
- *
- * @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
- * @param pixels an {@link java.nio.ByteBuffer} of pixel data to write.
- * @param offset the offset of the raw image in bytes. This indicates how many bytes will
- * be skipped in the input before any pixel data is read.
- *
- * @throws IOException if an error was encountered in the input or output stream.
- * @throws java.lang.IllegalStateException if not enough metadata information has been
- * set to write a well-formatted DNG file.
- */
- public void writeByteBuffer(OutputStream dngOutput, Size size, ByteBuffer pixels, long offset)
- throws IOException {
- if (dngOutput == null || pixels == null) {
- throw new NullPointerException("Null argument to writeImage");
- }
- nativeWriteByteBuffer(dngOutput, pixels, offset);
- }
-
- /**
- * Write the pixel data to a DNG file with the currently configured metadata.
- *
- * <p>
- * For this method to succeed, the {@link android.media.Image} input must contain
- * {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data, otherwise an
- * {@link java.lang.IllegalArgumentException} will be thrown.
- * </p>
- *
- * @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
- * @param pixels an {@link android.media.Image} to write.
- *
- * @throws java.io.IOException if an error was encountered in the output stream.
- * @throws java.lang.IllegalArgumentException if an image with an unsupported format was used.
- * @throws java.lang.IllegalStateException if not enough metadata information has been
- * set to write a well-formatted DNG file.
- */
- public void writeImage(OutputStream dngOutput, Image pixels) throws IOException {
- if (dngOutput == null || pixels == null) {
- throw new NullPointerException("Null argument to writeImage");
- }
-
- int format = pixels.getFormat();
- if (format != ImageFormat.RAW_SENSOR) {
- throw new IllegalArgumentException("Unsupported image format " + format);
- }
-
- Image.Plane[] planes = pixels.getPlanes();
- nativeWriteImage(dngOutput, pixels.getWidth(), pixels.getHeight(), planes[0].getBuffer(),
- planes[0].getRowStride(), planes[0].getPixelStride());
- }
-
- @Override
- public void close() {
- nativeDestroy();
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- close();
- } finally {
- super.finalize();
- }
- }
-
- /**
- * This field is used by native code, do not access or modify.
- */
- private long mNativeContext;
-
- private static native void nativeClassInit();
-
- private synchronized native void nativeInit(CameraMetadataNative nativeCharacteristics,
- CameraMetadataNative nativeResult);
-
- private synchronized native void nativeDestroy();
-
- private synchronized native void nativeSetOrientation(int orientation);
-
- private synchronized native void nativeSetThumbnailBitmap(Bitmap bitmap);
-
- private synchronized native void nativeSetThumbnailImage(int width, int height,
- ByteBuffer yBuffer, int yRowStride,
- int yPixStride, ByteBuffer uBuffer,
- int uRowStride, int uPixStride,
- ByteBuffer vBuffer, int vRowStride,
- int vPixStride);
-
- private synchronized native void nativeWriteImage(OutputStream out, int width, int height,
- ByteBuffer rawBuffer, int rowStride,
- int pixStride) throws IOException;
-
- private synchronized native void nativeWriteByteBuffer(OutputStream out, ByteBuffer rawBuffer,
- long offset) throws IOException;
-
- private synchronized native void nativeWriteInputStream(OutputStream out, InputStream rawStream,
- long offset) throws IOException;
-
- static {
- System.loadLibrary("media_jni");
- nativeClassInit();
- }
-}
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index d658654..90fe695 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,7 +2,6 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- android_media_DngCreator.cpp \
android_media_ImageReader.cpp \
android_media_MediaCrypto.cpp \
android_media_MediaCodec.cpp \
@@ -42,7 +41,6 @@
libjhead \
libexif \
libstagefright_amrnb_common \
- libimg_utils \
LOCAL_REQUIRED_MODULES := \
libjhead_jni
@@ -55,7 +53,6 @@
external/tremor/Tremor \
frameworks/base/core/jni \
frameworks/av/media/libmedia \
- frameworks/av/media/img_utils/include \
frameworks/av/media/libstagefright \
frameworks/av/media/libstagefright/codecs/amrnb/enc/src \
frameworks/av/media/libstagefright/codecs/amrnb/common \
diff --git a/media/jni/android_media_DngCreator.cpp b/media/jni/android_media_DngCreator.cpp
deleted file mode 100644
index 860d896..0000000
--- a/media/jni/android_media_DngCreator.cpp
+++ /dev/null
@@ -1,772 +0,0 @@
-/*
- * 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.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "DngCreator_JNI"
-
-#include <system/camera_metadata.h>
-#include <camera/CameraMetadata.h>
-#include <img_utils/DngUtils.h>
-#include <img_utils/TagDefinitions.h>
-#include <img_utils/TiffIfd.h>
-#include <img_utils/TiffWriter.h>
-#include <img_utils/Output.h>
-
-#include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/StrongPointer.h>
-#include <utils/RefBase.h>
-#include <cutils/properties.h>
-
-#include "android_runtime/AndroidRuntime.h"
-#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
-
-#include <jni.h>
-#include <JNIHelp.h>
-
-using namespace android;
-using namespace img_utils;
-
-#define BAIL_IF_INVALID(expr, jnienv, tagId) \
- if ((expr) != OK) { \
- jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
- "Invalid metadata for tag %x", tagId); \
- return; \
- }
-
-#define BAIL_IF_EMPTY(entry, jnienv, tagId) \
- if (entry.count == 0) { \
- jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
- "Missing metadata fields for tag %x", tagId); \
- return; \
- }
-
-#define ANDROID_MEDIA_DNGCREATOR_CTX_JNI_ID "mNativeContext"
-
-static struct {
- jfieldID mNativeContext;
-} gDngCreatorClassInfo;
-
-static struct {
- jmethodID mWriteMethod;
-} gOutputStreamClassInfo;
-
-enum {
- BITS_PER_SAMPLE = 16,
- BYTES_PER_SAMPLE = 2,
- TIFF_IFD_0 = 0
-};
-
-// ----------------------------------------------------------------------------
-
-// This class is not intended to be used across JNI calls.
-class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
-public:
- JniOutputStream(JNIEnv* env, jobject outStream);
-
- virtual ~JniOutputStream();
-
- status_t open();
- status_t write(const uint8_t* buf, size_t offset, size_t count);
- status_t close();
-private:
- enum {
- BYTE_ARRAY_LENGTH = 1024
- };
- jobject mOutputStream;
- JNIEnv* mEnv;
- jbyteArray mByteArray;
-};
-
-JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
- mEnv(env) {
- mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
- if (mByteArray == NULL) {
- jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
- }
-}
-
-JniOutputStream::~JniOutputStream() {
- mEnv->DeleteLocalRef(mByteArray);
-}
-
-status_t JniOutputStream::open() {
- // Do nothing
- return OK;
-}
-
-status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
- while(count > 0) {
- size_t len = BYTE_ARRAY_LENGTH;
- len = (count > len) ? len : count;
- mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
-
- if (mEnv->ExceptionCheck()) {
- return BAD_VALUE;
- }
-
- mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
- 0, len);
-
- if (mEnv->ExceptionCheck()) {
- return BAD_VALUE;
- }
-
- count -= len;
- offset += len;
- }
- return OK;
-}
-
-status_t JniOutputStream::close() {
- // Do nothing
- return OK;
-}
-
-// ----------------------------------------------------------------------------
-
-extern "C" {
-
-static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
- ALOGV("%s:", __FUNCTION__);
- return reinterpret_cast<TiffWriter*>(env->GetLongField(thiz,
- gDngCreatorClassInfo.mNativeContext));
-}
-
-static void DngCreator_setCreator(JNIEnv* env, jobject thiz, sp<TiffWriter> writer) {
- ALOGV("%s:", __FUNCTION__);
- TiffWriter* current = DngCreator_getCreator(env, thiz);
- if (writer != NULL) {
- writer->incStrong((void*) DngCreator_setCreator);
- }
- if (current) {
- current->decStrong((void*) DngCreator_setCreator);
- }
- env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
- reinterpret_cast<jlong>(writer.get()));
-}
-
-static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
- ALOGV("%s:", __FUNCTION__);
-
- gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
- ANDROID_MEDIA_DNGCREATOR_CTX_JNI_ID, "J");
- LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
- "can't find android/media/DngCreator.%s", ANDROID_MEDIA_DNGCREATOR_CTX_JNI_ID);
-
- jclass outputStreamClazz = env->FindClass("java/io/OutputStream");
- LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
- gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
- LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
-}
-
-static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
- jobject resultsPtr) {
- ALOGV("%s:", __FUNCTION__);
- CameraMetadata characteristics;
- CameraMetadata results;
- if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
- jniThrowException(env, "java/lang/AssertionError",
- "No native metadata defined for camera characteristics.");
- return;
- }
- if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
- jniThrowException(env, "java/lang/AssertionError",
- "No native metadata defined for capture results.");
- return;
- }
-
- sp<TiffWriter> writer = new TiffWriter();
-
- writer->addIfd(TIFF_IFD_0);
-
- status_t err = OK;
-
- const uint32_t samplesPerPixel = 1;
- const uint32_t bitsPerSample = BITS_PER_SAMPLE;
- const uint32_t bitsPerByte = BITS_PER_SAMPLE / BYTES_PER_SAMPLE;
- uint32_t imageWidth = 0;
- uint32_t imageHeight = 0;
-
- OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
-
- // TODO: Greensplit.
- // TODO: UniqueCameraModel
- // TODO: Add remaining non-essential tags
- {
- // Set orientation
- uint16_t orientation = 1; // Normal
- BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
- TAG_ORIENTATION);
- }
-
- {
- // Set subfiletype
- uint32_t subfileType = 0; // Main image
- BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
- TAG_NEWSUBFILETYPE);
- }
-
- {
- // Set bits per sample
- uint16_t bits = static_cast<uint16_t>(bitsPerSample);
- BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
- TAG_BITSPERSAMPLE);
- }
-
- {
- // Set compression
- uint16_t compression = 1; // None
- BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
- TAG_COMPRESSION);
- }
-
- {
- // Set dimensions
- camera_metadata_entry entry =
- characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH);
- uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
- uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
- BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
- TAG_IMAGEWIDTH);
- BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
- TAG_IMAGELENGTH);
- imageWidth = width;
- imageHeight = height;
- }
-
- {
- // Set photometric interpretation
- uint16_t interpretation = 32803;
- BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
- TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION);
- }
-
- {
- // Set blacklevel tags
- camera_metadata_entry entry =
- characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
- BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL);
- const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
- BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
- TAG_BLACKLEVEL);
-
- uint16_t repeatDim[2] = {2, 2};
- BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
- TAG_BLACKLEVELREPEATDIM);
- }
-
- {
- // Set samples per pixel
- uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
- BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
- env, TAG_SAMPLESPERPIXEL);
- }
-
- {
- // Set planar configuration
- uint16_t config = 1; // Chunky
- BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
- env, TAG_PLANARCONFIGURATION);
- }
-
- {
- // Set CFA pattern dimensions
- uint16_t repeatDim[2] = {2, 2};
- BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
- env, TAG_CFAREPEATPATTERNDIM);
- }
-
- {
- // Set CFA pattern
- camera_metadata_entry entry =
- characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
- BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN);
- camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
- static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
- entry.data.u8[0]);
- switch(cfa) {
- case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
- uint8_t cfa[4] = {0, 1, 1, 2};
- BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
- env, TAG_CFAPATTERN);
- opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
- break;
- }
- case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
- uint8_t cfa[4] = {1, 0, 2, 1};
- BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
- env, TAG_CFAPATTERN);
- opcodeCfaLayout = OpcodeListBuilder::CFA_GRBG;
- break;
- }
- case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
- uint8_t cfa[4] = {1, 2, 0, 1};
- BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
- env, TAG_CFAPATTERN);
- opcodeCfaLayout = OpcodeListBuilder::CFA_GBRG;
- break;
- }
- case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
- uint8_t cfa[4] = {2, 1, 1, 0};
- BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
- env, TAG_CFAPATTERN);
- opcodeCfaLayout = OpcodeListBuilder::CFA_BGGR;
- break;
- }
- default: {
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
- "Invalid metadata for tag %d", TAG_CFAPATTERN);
- return;
- }
- }
- }
-
- {
- // Set CFA plane color
- uint8_t cfaPlaneColor[3] = {0, 1, 2};
- BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
- env, TAG_CFAPLANECOLOR);
- }
-
- {
- // Set CFA layout
- uint16_t cfaLayout = 1;
- BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
- env, TAG_CFALAYOUT);
- }
-
- {
- // Set DNG version information
- uint8_t version[4] = {1, 4, 0, 0};
- BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
- env, TAG_DNGVERSION);
-
- uint8_t backwardVersion[4] = {1, 1, 0, 0};
- BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
- env, TAG_DNGBACKWARDVERSION);
- }
-
- {
- // Set whitelevel
- camera_metadata_entry entry =
- characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
- BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL);
- uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
- BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
- TAG_WHITELEVEL);
- }
-
- {
- // Set default scale
- uint32_t defaultScale[4] = {1, 1, 1, 1};
- BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
- env, TAG_DEFAULTSCALE);
- }
-
- bool singleIlluminant = false;
- {
- // Set calibration illuminants
- camera_metadata_entry entry1 =
- characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
- BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1);
- camera_metadata_entry entry2 =
- characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
- if (entry2.count == 0) {
- singleIlluminant = true;
- }
- uint16_t ref1 = entry1.data.u8[0];
-
- BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
- TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1);
-
- if (!singleIlluminant) {
- uint16_t ref2 = entry2.data.u8[0];
- BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
- TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2);
- }
- }
-
- {
- // Set color transforms
- camera_metadata_entry entry1 =
- characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
- BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1);
-
- int32_t colorTransform1[entry1.count * 2];
-
- size_t ctr = 0;
- for(size_t i = 0; i < entry1.count; ++i) {
- colorTransform1[ctr++] = entry1.data.r[i].numerator;
- colorTransform1[ctr++] = entry1.data.r[i].denominator;
- }
-
- BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1, TIFF_IFD_0),
- env, TAG_COLORMATRIX1);
-
- if (!singleIlluminant) {
- camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
- BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2);
- int32_t colorTransform2[entry2.count * 2];
-
- ctr = 0;
- for(size_t i = 0; i < entry2.count; ++i) {
- colorTransform2[ctr++] = entry2.data.r[i].numerator;
- colorTransform2[ctr++] = entry2.data.r[i].denominator;
- }
-
- BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2, TIFF_IFD_0),
- env, TAG_COLORMATRIX2);
- }
- }
-
- {
- // Set calibration transforms
- camera_metadata_entry entry1 =
- characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
- BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1);
-
- int32_t calibrationTransform1[entry1.count * 2];
-
- size_t ctr = 0;
- for(size_t i = 0; i < entry1.count; ++i) {
- calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
- calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
- }
-
- BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, calibrationTransform1,
- TIFF_IFD_0), env, TAG_CAMERACALIBRATION1);
-
- if (!singleIlluminant) {
- camera_metadata_entry entry2 =
- characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
- BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2);
- int32_t calibrationTransform2[entry2.count * 2];
-
- ctr = 0;
- for(size_t i = 0; i < entry2.count; ++i) {
- calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
- calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
- }
-
- BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, calibrationTransform1,
- TIFF_IFD_0), env, TAG_CAMERACALIBRATION2);
- }
- }
-
- {
- // Set forward transforms
- camera_metadata_entry entry1 =
- characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
- BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1);
-
- int32_t forwardTransform1[entry1.count * 2];
-
- size_t ctr = 0;
- for(size_t i = 0; i < entry1.count; ++i) {
- forwardTransform1[ctr++] = entry1.data.r[i].numerator;
- forwardTransform1[ctr++] = entry1.data.r[i].denominator;
- }
-
- BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
- TIFF_IFD_0), env, TAG_FORWARDMATRIX1);
-
- if (!singleIlluminant) {
- camera_metadata_entry entry2 =
- characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
- BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2);
- int32_t forwardTransform2[entry2.count * 2];
-
- ctr = 0;
- for(size_t i = 0; i < entry2.count; ++i) {
- forwardTransform2[ctr++] = entry2.data.r[i].numerator;
- forwardTransform2[ctr++] = entry2.data.r[i].denominator;
- }
-
- BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
- TIFF_IFD_0), env, TAG_FORWARDMATRIX2);
- }
- }
-
- {
- // Set camera neutral
- camera_metadata_entry entry =
- results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
- BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL);
- uint32_t cameraNeutral[entry.count * 2];
-
- size_t ctr = 0;
- for(size_t i = 0; i < entry.count; ++i) {
- cameraNeutral[ctr++] =
- static_cast<uint32_t>(entry.data.r[i].numerator);
- cameraNeutral[ctr++] =
- static_cast<uint32_t>(entry.data.r[i].denominator);
- }
-
- BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
- TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL);
- }
-
- {
- // Setup data strips
- // TODO: Switch to tiled implementation.
- uint32_t offset = 0;
- BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &offset, TIFF_IFD_0), env,
- TAG_STRIPOFFSETS);
-
- BAIL_IF_INVALID(writer->addEntry(TAG_ROWSPERSTRIP, 1, &imageHeight, TIFF_IFD_0), env,
- TAG_ROWSPERSTRIP);
-
- uint32_t byteCount = imageWidth * imageHeight * bitsPerSample * samplesPerPixel /
- bitsPerByte;
- BAIL_IF_INVALID(writer->addEntry(TAG_STRIPBYTECOUNTS, 1, &byteCount, TIFF_IFD_0), env,
- TAG_STRIPBYTECOUNTS);
- }
-
- {
- // Setup default crop + crop origin tags
- uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
- uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
- if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
- uint32_t defaultCropOrigin[] = {margin, margin};
- uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
- BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
- TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN);
- BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
- TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE);
- }
- }
-
- {
- // Setup unique camera model tag
- char model[PROPERTY_VALUE_MAX];
- property_get("ro.product.model", model, "");
-
- char manufacturer[PROPERTY_VALUE_MAX];
- property_get("ro.product.manufacturer", manufacturer, "");
-
- char brand[PROPERTY_VALUE_MAX];
- property_get("ro.product.brand", brand, "");
-
- String8 cameraModel(model);
- cameraModel += "-";
- cameraModel += manufacturer;
- cameraModel += "-";
- cameraModel += brand;
-
- BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
- reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
- TAG_UNIQUECAMERAMODEL);
- }
-
- {
- // Setup opcode List 2
- camera_metadata_entry entry1 =
- characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
- BAIL_IF_EMPTY(entry1, env, TAG_OPCODELIST2);
- uint32_t lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
- uint32_t lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
-
- camera_metadata_entry entry2 =
- results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
- BAIL_IF_EMPTY(entry2, env, TAG_OPCODELIST2);
- if (entry2.count == lsmWidth * lsmHeight * 4) {
-
- OpcodeListBuilder builder;
- status_t err = builder.addGainMapsForMetadata(lsmWidth,
- lsmHeight,
- 0,
- 0,
- imageHeight,
- imageWidth,
- opcodeCfaLayout,
- entry2.data.f);
- if (err == OK) {
- size_t listSize = builder.getSize();
- uint8_t opcodeListBuf[listSize];
- err = builder.buildOpList(opcodeListBuf);
- if (err == OK) {
- BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
- TIFF_IFD_0), env, TAG_OPCODELIST2);
- } else {
- ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
- jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
- }
- } else {
- ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
- jniThrowRuntimeException(env, "failed to add lens shading map.");
- }
- } else {
- ALOGW("%s: Lens shading map not present in results, skipping...", __FUNCTION__);
- }
- }
-
- DngCreator_setCreator(env, thiz, writer);
-}
-
-static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
- ALOGV("%s:", __FUNCTION__);
- DngCreator_setCreator(env, thiz, NULL);
-}
-
-static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz) {
- ALOGV("%s:", __FUNCTION__);
- jniThrowRuntimeException(env, "nativeSetOrientation is not implemented");
-}
-
-static void DngCreator_nativeSetThumbnailBitmap(JNIEnv* env, jobject thiz, jobject bitmap) {
- ALOGV("%s:", __FUNCTION__);
- jniThrowRuntimeException(env, "nativeSetThumbnailBitmap is not implemented");
-}
-
-static void DngCreator_nativeSetThumbnailImage(JNIEnv* env, jobject thiz, jint width, jint height,
- jobject yBuffer, jint yRowStride, jint yPixStride, jobject uBuffer, jint uRowStride,
- jint uPixStride, jobject vBuffer, jint vRowStride, jint vPixStride) {
- ALOGV("%s:", __FUNCTION__);
- jniThrowRuntimeException(env, "nativeSetThumbnailImage is not implemented");
-}
-
-static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
- jint height, jobject inBuffer, jint rowStride, jint pixStride) {
- ALOGV("%s:", __FUNCTION__);
-
- sp<JniOutputStream> out = new JniOutputStream(env, outStream);
- if(env->ExceptionCheck()) {
- ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
- return;
- }
-
- uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
- if (pixelBytes == NULL) {
- ALOGE("%s: Could not get native byte buffer", __FUNCTION__);
- jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid bytebuffer");
- return;
- }
-
- TiffWriter* writer = DngCreator_getCreator(env, thiz);
- if (writer == NULL) {
- ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
- jniThrowException(env, "java/lang/AssertionError",
- "Write called with uninitialized DngCreator");
- return;
- }
- // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
- uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, TIFF_IFD_0)->getData<uint32_t>());
- uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, TIFF_IFD_0)->getData<uint32_t>());
- if (metadataWidth != width) {
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
- "Metadata width %d doesn't match image width %d", metadataWidth, width);
- return;
- }
-
- if (metadataHeight != height) {
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
- "Metadata height %d doesn't match image height %d", metadataHeight, height);
- return;
- }
-
- uint32_t stripOffset = writer->getTotalSize();
-
- BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &stripOffset, TIFF_IFD_0), env,
- TAG_STRIPOFFSETS);
-
- if (writer->write(out.get()) != OK) {
- if (!env->ExceptionCheck()) {
- jniThrowException(env, "java/io/IOException", "Failed to write metadata");
- }
- return;
- }
-
- size_t fullSize = rowStride * height;
- jlong capacity = env->GetDirectBufferCapacity(inBuffer);
- if (capacity < 0 || fullSize > capacity) {
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
- "Invalid size %d for Image, size given in metadata is %d at current stride",
- capacity, fullSize);
- return;
- }
-
- if (pixStride == BYTES_PER_SAMPLE && rowStride == width * BYTES_PER_SAMPLE) {
- if (out->write(pixelBytes, 0, fullSize) != OK || env->ExceptionCheck()) {
- if (!env->ExceptionCheck()) {
- jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
- }
- return;
- }
- } else if (pixStride == BYTES_PER_SAMPLE) {
- for (size_t i = 0; i < height; ++i) {
- if (out->write(pixelBytes, i * rowStride, pixStride * width) != OK ||
- env->ExceptionCheck()) {
- if (!env->ExceptionCheck()) {
- jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
- }
- return;
- }
- }
- } else {
- for (size_t i = 0; i < height; ++i) {
- for (size_t j = 0; j < width; ++j) {
- if (out->write(pixelBytes, i * rowStride + j * pixStride,
- BYTES_PER_SAMPLE) != OK || !env->ExceptionCheck()) {
- if (env->ExceptionCheck()) {
- jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
- }
- return;
- }
- }
- }
- }
-
-}
-
-static void DngCreator_nativeWriteByteBuffer(JNIEnv* env, jobject thiz, jobject outStream,
- jobject rawBuffer, jlong offset) {
- ALOGV("%s:", __FUNCTION__);
- jniThrowRuntimeException(env, "nativeWriteByteBuffer is not implemented.");
-}
-
-static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
- jobject inStream, jlong offset) {
- ALOGV("%s:", __FUNCTION__);
- jniThrowRuntimeException(env, "nativeWriteInputStream is not implemented.");
-}
-
-} /*extern "C" */
-
-static JNINativeMethod gDngCreatorMethods[] = {
- {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
- {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
- "Landroid/hardware/camera2/impl/CameraMetadataNative;)V", (void*) DngCreator_init},
- {"nativeDestroy", "()V", (void*) DngCreator_destroy},
- {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
- {"nativeSetThumbnailBitmap","(Landroid/graphics/Bitmap;)V",
- (void*) DngCreator_nativeSetThumbnailBitmap},
- {"nativeSetThumbnailImage",
- "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V",
- (void*) DngCreator_nativeSetThumbnailImage},
- {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;II)V",
- (void*) DngCreator_nativeWriteImage},
- {"nativeWriteByteBuffer", "(Ljava/io/OutputStream;Ljava/nio/ByteBuffer;J)V",
- (void*) DngCreator_nativeWriteByteBuffer},
- {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;J)V",
- (void*) DngCreator_nativeWriteInputStream},
-};
-
-int register_android_media_DngCreator(JNIEnv *env) {
- return AndroidRuntime::registerNativeMethods(env,
- "android/media/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
-}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 9d03cc38..6f42057 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -884,7 +884,6 @@
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
-extern int register_android_media_DngCreator(JNIEnv *env);
extern int register_android_media_ImageReader(JNIEnv *env);
extern int register_android_media_Crypto(JNIEnv *env);
extern int register_android_media_Drm(JNIEnv *env);
@@ -914,11 +913,6 @@
}
assert(env != NULL);
- if (register_android_media_DngCreator(env) < 0) {
- ALOGE("ERROR: ImageReader native registration failed");
- goto bail;
- }
-
if (register_android_media_ImageReader(env) < 0) {
ALOGE("ERROR: ImageReader native registration failed");
goto bail;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index a77b647..a3caba2 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -17,6 +17,7 @@
package com.android.mediaframeworktest.unit;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
import android.util.Range;
import android.util.Rational;
import android.util.SizeF;
@@ -26,6 +27,8 @@
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.util.Size;
import android.hardware.camera2.impl.CameraMetadataNative;
@@ -46,6 +49,7 @@
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.List;
/**
* <pre>
@@ -56,6 +60,10 @@
*/
public class CameraMetadataTest extends junit.framework.TestCase {
+ private static final boolean VERBOSE = false;
+ private static final String TAG = "CameraMetadataTest";
+
+
CameraMetadataNative mMetadata;
// Sections
@@ -940,7 +948,7 @@
};
int availableFormatTag = CameraMetadataNative.getTag("android.scaler.availableFormats");
- Key<int[]> formatKey = CameraCharacteristics.SCALER_AVAILABLE_FORMATS;
+ Key<int[]> formatKey = CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey();
validateArrayMetadataReadWriteOverride(formatKey, availableFormats,
expectedIntValues, availableFormatTag);
@@ -1046,7 +1054,7 @@
0x20, 320, 240, INPUT, // RAW16
};
Key<StreamConfiguration[]> configKey =
- CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS.getNativeKey();
mMetadata.writeValues(configKey.getTag(),
toByteArray(rawAvailableStreamConfigs));
@@ -1074,7 +1082,7 @@
0x21, 1920, 1080, 33333338 // BLOB
};
Key<StreamConfigurationDuration[]> durationKey =
- CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS;
+ CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS.getNativeKey();
mMetadata.writeValues(durationKey.getTag(),
toByteArray(rawAvailableMinDurations));
@@ -1100,7 +1108,7 @@
0x21, 1920, 1080, 33333338 // BLOB
};
Key<StreamConfigurationDuration[]> stallDurationKey =
- CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS;
+ CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS.getNativeKey();
mMetadata.writeValues(stallDurationKey.getTag(),
toByteArray(rawAvailableStallDurations));
@@ -1158,6 +1166,31 @@
}
}
+ @SmallTest
+ public void testCaptureResult() {
+ mMetadata.set(CaptureRequest.CONTROL_AE_MODE,
+ CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH);
+
+ if (VERBOSE) mMetadata.dumpToLog();
+
+ CaptureResult captureResult = new CaptureResult(mMetadata, /*sequenceId*/0);
+
+ List<CaptureResult.Key<?>> allKeys = captureResult.getKeys();
+ if (VERBOSE) Log.v(TAG, "testCaptureResult: key list size " + allKeys);
+ for (CaptureResult.Key<?> key : captureResult.getKeys()) {
+ if (VERBOSE) {
+ Log.v(TAG,
+ "testCaptureResult: key " + key + " value" + captureResult.get(key));
+ }
+ }
+
+ assertTrue(allKeys.size() >= 1); // FIXME: android.statistics.faces counts as a key
+ assertTrue(allKeys.contains(CaptureResult.CONTROL_AE_MODE));
+
+ assertEquals(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH,
+ (int)captureResult.get(CaptureResult.CONTROL_AE_MODE));
+ }
+
private static void checkStreamConfigurationMapByFormatSize(StreamConfigurationMap configMap,
int format, int width, int height,
boolean output) {
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index acfcd83..3f37ed1 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -171,8 +171,3 @@
{
return static_cast<Sensor const*>(sensor)->getStringType().string();
}
-
-const char* ASensor_getRequiredPermission(ASensor const* sensor)
-{
- return static_cast<Sensor const*>(sensor)->getRequiredPermission().string();
-}
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index f0f7e10..24d66ce 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -27,7 +27,7 @@
<string name="menu_settings" msgid="6008033148948428823">"Ρυθμίσεις"</string>
<string name="menu_open" msgid="432922957274920903">"Άνοιγμα"</string>
<string name="menu_save" msgid="2394743337684426338">"Αποθήκευση"</string>
- <string name="menu_share" msgid="3075149983979628146">"Κοινοποίηση"</string>
+ <string name="menu_share" msgid="3075149983979628146">"Κοινή χρήση"</string>
<string name="menu_delete" msgid="8138799623850614177">"Διαγραφή"</string>
<string name="menu_select" msgid="8711270657353563424">"Επιλογή \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"Επιλέχθηκαν <xliff:g id="COUNT">%1$d</xliff:g>"</string>
@@ -51,5 +51,5 @@
<string name="empty" msgid="7858882803708117596">"Δεν υπάρχουν στοιχεία"</string>
<string name="toast_no_application" msgid="1339885974067891667">"Δεν είναι δυνατό το άνοιγμα του αρχείου"</string>
<string name="toast_failed_delete" msgid="2180678019407244069">"Δεν είναι δυνατή η διαγραφή ορισμένων εγγράφων"</string>
- <string name="share_via" msgid="8966594246261344259">"Κοινοποίηση μέσω"</string>
+ <string name="share_via" msgid="8966594246261344259">"Κοινή χρήση μέσω"</string>
</resources>
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index 45d1e2a..d67a9fd 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreeus"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litaus"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spaans (Latyn)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letties"</string>
</resources>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index 3f62df6..3e84794 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ዕብራስጥ"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ሊቱዌኒያኛ"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ስፓኒሽ (ላቲን)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ላትቪያኛ"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index e70cad4..a922a46 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"العبرية"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"الليتوانية"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"الإسبانية (اللاتينية)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"اللاتفية"</string>
</resources>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index 7582a69..d68a347 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ивритска клавиатурна подредба"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Литовска клавиатурна подредба"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Исп. клав. подредба (Лат. Америка)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"латвийски"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index e38b9a8..6baa5b8 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreu"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituà"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espanyol (llatí)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letó"</string>
</resources>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index 532f3c0..1c502fe 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrejština"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litevština"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španělština (Latinská Amerika)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lotyšská klávesnice"</string>
</resources>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index 3c907f8..043a5b3 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebræisk"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litauisk"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spansk (latinamerika)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lettisk"</string>
</resources>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index 83be031..04c19e3 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebräisch"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litauisch"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanisch (Lateinisch)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lettisch"</string>
</resources>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index 99f77ed..025a288 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Εβραϊκά"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Λιθουανικά"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Ισπανικά (Λατινικής Αμερικής)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Λετονικά"</string>
</resources>
diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml
index e22c675..d5797a0 100644
--- a/packages/InputDevices/res/values-en-rGB/strings.xml
+++ b/packages/InputDevices/res/values-en-rGB/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrew"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lithuanian"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string>
</resources>
diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml
index e22c675..d5797a0 100644
--- a/packages/InputDevices/res/values-en-rIN/strings.xml
+++ b/packages/InputDevices/res/values-en-rIN/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrew"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lithuanian"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string>
</resources>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index 5190fc5..0a9d2f3 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreo"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Español (latino)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letón"</string>
</resources>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index a81ed29..6e41abf 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreo"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Español (Latinoamérica)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letón"</string>
</resources>
diff --git a/packages/InputDevices/res/values-et-rEE/strings.xml b/packages/InputDevices/res/values-et-rEE/strings.xml
index b90f825..0d931ce 100644
--- a/packages/InputDevices/res/values-et-rEE/strings.xml
+++ b/packages/InputDevices/res/values-et-rEE/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Heebrea"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Leedu"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Hispaania (Ladina-Ameerika)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"läti keel"</string>
</resources>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index 490b5c2..e87fbad 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"عبری"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"لیتوانیایی"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"اسپانیایی (لاتین)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"لتونیایی"</string>
</resources>
diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml
index 060d0e7..5b39dfd 100644
--- a/packages/InputDevices/res/values-fi/strings.xml
+++ b/packages/InputDevices/res/values-fi/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"heprea"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"liettua"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"espanja (Latinalainen Amerikka)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvialainen"</string>
</resources>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index 8fc9f79..9973918 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hébreu"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituanien"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espagnol (latin)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letton"</string>
</resources>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index b029b02..fa2977b 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hébreu"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituanien"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espagnol (latin)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letton"</string>
</resources>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 8398c92..77cb8fe 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"हिब्रू"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"लिथुआनियाई"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"स्पेनिश (लैटिन)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"लातवियाई"</string>
</resources>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index 68c868f..bad973d 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrejski"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litavski"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španjolski (Latinska Amerika)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvijska"</string>
</resources>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index af6a571..510591d 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"héber"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litván"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"spanyol (latin-amerikai)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"lett"</string>
</resources>
diff --git a/packages/InputDevices/res/values-hy-rAM/strings.xml b/packages/InputDevices/res/values-hy-rAM/strings.xml
index 068e559..9ffa8bb 100644
--- a/packages/InputDevices/res/values-hy-rAM/strings.xml
+++ b/packages/InputDevices/res/values-hy-rAM/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Եբրայերեն"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Լիտվերեն"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Իսպաներեն (Լատինական)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"լատիշերեն"</string>
</resources>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index d32ac98..fccfa67 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ibrani"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lithuania"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanyol (Latin)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvi"</string>
</resources>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index 280a23c..83dba70 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ebraico"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spagnolo (America Latina)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lettone"</string>
</resources>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index 9bb66d8..26fe662 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"עברית"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ליטאית"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ספרדית (לטינית)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"לטבית"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index 26b1094..e2b154d 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ヘブライ語"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"リトアニア語"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"スペイン語(中南米)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ラトビア語"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ka-rGE/strings.xml b/packages/InputDevices/res/values-ka-rGE/strings.xml
index 35085a1..eff4b04 100644
--- a/packages/InputDevices/res/values-ka-rGE/strings.xml
+++ b/packages/InputDevices/res/values-ka-rGE/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ებრაული"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ლიტვური"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ესპანური (ლათინური)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ლატვიური"</string>
</resources>
diff --git a/packages/InputDevices/res/values-km-rKH/strings.xml b/packages/InputDevices/res/values-km-rKH/strings.xml
index ea3b755..60a28b1 100644
--- a/packages/InputDevices/res/values-km-rKH/strings.xml
+++ b/packages/InputDevices/res/values-km-rKH/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"អ៊ីស្រាអែល"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"លីទុយអានី"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"អេស្ប៉ាញ (ឡាតាំង)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ឡាតវីយ៉ា"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml
index e360ed0..3f563d1 100644
--- a/packages/InputDevices/res/values-ko/strings.xml
+++ b/packages/InputDevices/res/values-ko/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"히브리어"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"리투아니아어"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"스페인어(라틴)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"라트비아어"</string>
</resources>
diff --git a/packages/InputDevices/res/values-lo-rLA/strings.xml b/packages/InputDevices/res/values-lo-rLA/strings.xml
index fc37501..fb3fe17 100644
--- a/packages/InputDevices/res/values-lo-rLA/strings.xml
+++ b/packages/InputDevices/res/values-lo-rLA/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ຮີບຣິວ"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ລິທົວນຽນ"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ສະແປນນິດ (ລາຕິນ)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ລັດວຽນ"</string>
</resources>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index c197389..d0eb1f6 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrajų"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lietuvių"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Ispanų (Lotynų Amerika)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvių k."</string>
</resources>
diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml
index 53d2467..0608bf0 100644
--- a/packages/InputDevices/res/values-lv/strings.xml
+++ b/packages/InputDevices/res/values-lv/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ivrits"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lietuviešu"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spāņu (latīņu)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latviešu"</string>
</resources>
diff --git a/packages/InputDevices/res/values-mn-rMN/strings.xml b/packages/InputDevices/res/values-mn-rMN/strings.xml
index 194c577..a28fd2a 100644
--- a/packages/InputDevices/res/values-mn-rMN/strings.xml
+++ b/packages/InputDevices/res/values-mn-rMN/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Еврей"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Литви"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Испани (Латин)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Латви"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ms-rMY/strings.xml b/packages/InputDevices/res/values-ms-rMY/strings.xml
index a3098c5..a1a6d00 100644
--- a/packages/InputDevices/res/values-ms-rMY/strings.xml
+++ b/packages/InputDevices/res/values-ms-rMY/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Bahasa Ibrani"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Bahasa Lithuania"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Bahasa Sepanyol (Latin)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Bahasa Latvia"</string>
</resources>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index 47cff5c..ad4b704 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebraisk"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litauisk"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spansk (latinsk)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvisk"</string>
</resources>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index e858c9c..c57251e 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreeuws"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litouws"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spaans (Latijns-Amerika)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lets"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index 0ca91ca..39fb3ec 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrajski"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litewski"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"hiszpański (Ameryka Łacińska)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"łotewski"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index d5afe05..3ac3b84 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebraico"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espanhol (América Latina)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letão"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index 84cf865..e9a0a38 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebraico"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espanhol (América Latina)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letão"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index 5278643..c2392b1 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ebraică"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituaniană"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spaniolă (America Latină)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letonă"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 3ae2e53..70ecf6e 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Иврит"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Литовский"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Испанский (Латинская Америка)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"латышский"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index 1e51167..d2ee0cf 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrejčina"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litovčina"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Španielčina (Latinská Amerika)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lotyština"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index 66e340e..38542ef 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrejščina"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litovščina"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španščina (Latinska Amerika)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvijščina"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml
index 14ccbf3..dd500e6 100644
--- a/packages/InputDevices/res/values-sr/strings.xml
+++ b/packages/InputDevices/res/values-sr/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"хебрејски"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"литвански"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"шпански (Латинска Америка)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"летонски"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index f3338c6..c2406c0 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreiska"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litauiska"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanska (latinamerikansk)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"lettiska"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index 336cc33..f71a696 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Kiyahudi"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Kilithuania"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Kihispania (Kilatini)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Kilatvia"</string>
</resources>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index 86a633a..296994b 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ฮิบรู"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ลิทัวเนีย"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"สเปน (ละติน)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ลัตเวีย"</string>
</resources>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index 702a0cb..d7920ed 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrew"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lithuanian"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string>
</resources>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index b3ce0a3..c0c70be 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"İbranice"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litvanca"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"İspanyolca (Latin)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letonca"</string>
</resources>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index 5193a90..d8152d4 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Іврит"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Литовська"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Іспанська (латиниця)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Латвійська"</string>
</resources>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index 2240100..a90c1cd 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Tiếng Do Thái"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Tiếng Lithuania"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Tiếng Tây Ban Nha (La tinh)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Tiếng Latvia"</string>
</resources>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index 161b600..206f97c 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"希伯来语"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"立陶宛语"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"西班牙语(拉丁美洲)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"拉脱维亚语"</string>
</resources>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index f0df88c..ff5570e 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"希伯來文"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"立陶宛文"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"西班牙文 (拉丁美洲)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"拉脫維亞文"</string>
</resources>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index ca43326..859983d 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"希伯來文"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"立陶宛文"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"西班牙文 (拉丁美洲)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"拉脫維亞文"</string>
</resources>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index 677ed294..9f30f7a 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -39,6 +39,5 @@
<string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Isi-Hebrew"</string>
<string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Isi-Lithuanian"</string>
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Isi-Spanish (Latin)"</string>
- <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
- <skip />
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Isi-Latvian"</string>
</resources>
diff --git a/packages/Keyguard/res/layout/keyguard_bouncer.xml b/packages/Keyguard/res/layout/keyguard_bouncer.xml
index 975a139..489c5f5 100644
--- a/packages/Keyguard/res/layout/keyguard_bouncer.xml
+++ b/packages/Keyguard/res/layout/keyguard_bouncer.xml
@@ -18,12 +18,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <View android:id="@+id/bouncer_background"
- android:background="#cc000000"
- android:clickable="true"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
<include
style="@style/BouncerSecurityContainer"
layout="@layout/keyguard_simple_host_view"
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml
index f79819f..112e371a 100644
--- a/packages/Keyguard/res/layout/keyguard_status_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_status_view.xml
@@ -39,13 +39,12 @@
android:id="@+id/clock_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|top"
+ android:layout_gravity="center_horizontal"
android:textColor="@color/clock_white"
android:singleLine="true"
style="@style/widget_big_thin"
android:format12Hour="@string/keyguard_widget_12_hours_format"
android:format24Hour="@string/keyguard_widget_24_hours_format"
- android:baselineAligned="true"
android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
<include layout="@layout/keyguard_status_area" />
diff --git a/packages/Keyguard/res/values-sw600dp-land/dimens.xml b/packages/Keyguard/res/values-sw600dp-land/dimens.xml
index 5615ff7..13a6f62 100644
--- a/packages/Keyguard/res/values-sw600dp-land/dimens.xml
+++ b/packages/Keyguard/res/values-sw600dp-land/dimens.xml
@@ -26,5 +26,4 @@
<!-- Overload default clock widget parameters -->
<dimen name="widget_big_font_size">88dp</dimen>
- <dimen name="bottom_text_spacing_digital">-24dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/Keyguard/res/values-sw600dp/dimens.xml b/packages/Keyguard/res/values-sw600dp/dimens.xml
index a5e93dc..b954792 100644
--- a/packages/Keyguard/res/values-sw600dp/dimens.xml
+++ b/packages/Keyguard/res/values-sw600dp/dimens.xml
@@ -65,7 +65,7 @@
<!-- Overload default clock widget parameters -->
<dimen name="widget_big_font_size">96dp</dimen>
<dimen name="widget_label_font_size">16sp</dimen>
- <dimen name="bottom_text_spacing_digital">-24dp</dimen>
+ <dimen name="bottom_text_spacing_digital">-8dp</dimen>
<!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
Should be 0 on devices with plenty of room (e.g. tablets) -->
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index 6224aed..3830df7 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -155,7 +155,7 @@
<dimen name="eca_overlap">-10dip</dimen>
<!-- Default clock parameters -->
- <dimen name="bottom_text_spacing_digital">-18dp</dimen>
+ <dimen name="bottom_text_spacing_digital">-6dp</dimen>
<dimen name="label_font_size">14dp</dimen>
<dimen name="widget_label_font_size">14sp</dimen>
<dimen name="widget_big_font_size">68dp</dimen>
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index d20b269..8cf07fa 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -97,9 +97,9 @@
<string name="keyguard_sim_unlock_progress_dialog_message">Unlocking SIM card\u2026</string>
<!-- Time format strings for fall-back clock widget -->
- <string name="keyguard_widget_12_hours_format" translatable="false">h:mm</string>
+ <string name="keyguard_widget_12_hours_format" translatable="false">h\uee01mm</string>
<!-- Time format strings for fall-back clock widget -->
- <string name="keyguard_widget_24_hours_format" translatable="false">kk:mm</string>
+ <string name="keyguard_widget_24_hours_format" translatable="false">kk\uee01mm</string>
<!-- Accessibility description sent when user changes the current lock screen widget. [CHAR_LIMIT=none] -->
<string name="keyguard_accessibility_widget_changed">%1$s. Widget %2$d of %3$d.</string>
diff --git a/packages/Keyguard/res/values/styles.xml b/packages/Keyguard/res/values/styles.xml
index 5ab00d2..11142cf 100644
--- a/packages/Keyguard/res/values/styles.xml
+++ b/packages/Keyguard/res/values/styles.xml
@@ -59,8 +59,6 @@
<!-- Built-in clock widget stuff -->
<style name="widget_label">
- <item name="android:textStyle">bold</item>
- <item name="android:fontFamily">sans-serif-light</item>
<item name="android:textSize">@dimen/widget_label_font_size</item>
</style>
<style name="big_thin">
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java
index 5d5168c..3f6ced6 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java
@@ -39,7 +39,7 @@
@Override
public void cleanUp() {
- // TODO Auto-generated method stub
+ getSecurityContainer().onPause();
}
@Override
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index ae55c4a..bef94fa 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -95,6 +95,10 @@
final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
setEnableMarquee(screenOn);
refresh();
+
+ // Disable elegant text height because our fancy colon makes the ymin value huge for no
+ // reason.
+ mClockView.setElegantTextHeight(false);
}
protected void refresh() {
@@ -164,6 +168,10 @@
clockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
+ // Use fancy colon.
+ clockView24 = clockView24.replace(':', '\uee01');
+ clockView12 = clockView12.replace(':', '\uee01');
+
cacheKey = key;
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
index a9206e7..48b7be9 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
@@ -237,11 +237,6 @@
if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
mSecurityContainer.showPrimarySecurityScreen(false);
mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON);
-
- // This is a an attempt to fix bug 7137389 where the device comes back on but the entire
- // layout is blank but forcing a layout causes it to reappear (e.g. with with
- // hierarchyviewer).
- requestLayout();
requestFocus();
}
diff --git a/packages/SettingsProvider/res/values-af/defaults.xml b/packages/SettingsProvider/res/values-af/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-af/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-am/defaults.xml b/packages/SettingsProvider/res/values-am/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-am/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-ar/defaults.xml b/packages/SettingsProvider/res/values-ar/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-ar/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-bg/defaults.xml b/packages/SettingsProvider/res/values-bg/defaults.xml
new file mode 100644
index 0000000..1d546ff
--- /dev/null
+++ b/packages/SettingsProvider/res/values-bg/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%2$s от %1$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-ca/defaults.xml b/packages/SettingsProvider/res/values-ca/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-ca/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-cs/defaults.xml b/packages/SettingsProvider/res/values-cs/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-cs/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-da/defaults.xml b/packages/SettingsProvider/res/values-da/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-da/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-de/defaults.xml b/packages/SettingsProvider/res/values-de/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-de/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-el/defaults.xml b/packages/SettingsProvider/res/values-el/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-el/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-en-rGB/defaults.xml b/packages/SettingsProvider/res/values-en-rGB/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-en-rGB/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-en-rIN/defaults.xml b/packages/SettingsProvider/res/values-en-rIN/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-en-rIN/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-es-rUS/defaults.xml b/packages/SettingsProvider/res/values-es-rUS/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-es-rUS/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-es/defaults.xml b/packages/SettingsProvider/res/values-es/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-es/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-et-rEE/defaults.xml b/packages/SettingsProvider/res/values-et-rEE/defaults.xml
new file mode 100644
index 0000000..5f99ed9
--- /dev/null
+++ b/packages/SettingsProvider/res/values-et-rEE/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%2$s, %1$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-fa/defaults.xml b/packages/SettingsProvider/res/values-fa/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-fa/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-fi/defaults.xml b/packages/SettingsProvider/res/values-fi/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-fi/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-fr-rCA/defaults.xml b/packages/SettingsProvider/res/values-fr-rCA/defaults.xml
new file mode 100644
index 0000000..1a04b0f
--- /dev/null
+++ b/packages/SettingsProvider/res/values-fr-rCA/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%2$s de %1$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-hi/defaults.xml b/packages/SettingsProvider/res/values-hi/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-hi/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-hr/defaults.xml b/packages/SettingsProvider/res/values-hr/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-hr/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-hu/defaults.xml b/packages/SettingsProvider/res/values-hu/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-hu/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-hy-rAM/defaults.xml b/packages/SettingsProvider/res/values-hy-rAM/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-hy-rAM/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-in/defaults.xml b/packages/SettingsProvider/res/values-in/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-in/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-it/defaults.xml b/packages/SettingsProvider/res/values-it/defaults.xml
new file mode 100644
index 0000000..bc995b0
--- /dev/null
+++ b/packages/SettingsProvider/res/values-it/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%2$s %1$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-iw/defaults.xml b/packages/SettingsProvider/res/values-iw/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-iw/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-ja/defaults.xml b/packages/SettingsProvider/res/values-ja/defaults.xml
new file mode 100644
index 0000000..bc995b0
--- /dev/null
+++ b/packages/SettingsProvider/res/values-ja/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%2$s %1$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-ka-rGE/defaults.xml b/packages/SettingsProvider/res/values-ka-rGE/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-ka-rGE/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-km-rKH/defaults.xml b/packages/SettingsProvider/res/values-km-rKH/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-km-rKH/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-ko/defaults.xml b/packages/SettingsProvider/res/values-ko/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-ko/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-lt/defaults.xml b/packages/SettingsProvider/res/values-lt/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-lt/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-lv/defaults.xml b/packages/SettingsProvider/res/values-lv/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-lv/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-ms-rMY/defaults.xml b/packages/SettingsProvider/res/values-ms-rMY/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-ms-rMY/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-nb/defaults.xml b/packages/SettingsProvider/res/values-nb/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-nb/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-nl/defaults.xml b/packages/SettingsProvider/res/values-nl/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-nl/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-pl/defaults.xml b/packages/SettingsProvider/res/values-pl/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-pl/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-pt-rPT/defaults.xml b/packages/SettingsProvider/res/values-pt-rPT/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-pt-rPT/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-pt/defaults.xml b/packages/SettingsProvider/res/values-pt/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-pt/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-ro/defaults.xml b/packages/SettingsProvider/res/values-ro/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-ro/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-ru/defaults.xml b/packages/SettingsProvider/res/values-ru/defaults.xml
new file mode 100644
index 0000000..bc995b0
--- /dev/null
+++ b/packages/SettingsProvider/res/values-ru/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%2$s %1$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-sk/defaults.xml b/packages/SettingsProvider/res/values-sk/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-sk/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-sl/defaults.xml b/packages/SettingsProvider/res/values-sl/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-sl/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-sr/defaults.xml b/packages/SettingsProvider/res/values-sr/defaults.xml
new file mode 100644
index 0000000..bc995b0
--- /dev/null
+++ b/packages/SettingsProvider/res/values-sr/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%2$s %1$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-sv/defaults.xml b/packages/SettingsProvider/res/values-sv/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-sv/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-sw/defaults.xml b/packages/SettingsProvider/res/values-sw/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-sw/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-th/defaults.xml b/packages/SettingsProvider/res/values-th/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-th/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-tl/defaults.xml b/packages/SettingsProvider/res/values-tl/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-tl/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-tr/defaults.xml b/packages/SettingsProvider/res/values-tr/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-tr/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-uk/defaults.xml b/packages/SettingsProvider/res/values-uk/defaults.xml
new file mode 100644
index 0000000..8ca4583
--- /dev/null
+++ b/packages/SettingsProvider/res/values-uk/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%2$s о %1$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-vi/defaults.xml b/packages/SettingsProvider/res/values-vi/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-vi/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-zh-rHK/defaults.xml b/packages/SettingsProvider/res/values-zh-rHK/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-zh-rHK/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-zh-rTW/defaults.xml b/packages/SettingsProvider/res/values-zh-rTW/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-zh-rTW/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-zu/defaults.xml b/packages/SettingsProvider/res/values-zu/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-zu/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6b62c25..e9cb197 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -52,6 +52,8 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
+ <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+
<!-- Physical hardware -->
<uses-permission android:name="android.permission.MANAGE_USB" />
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png
deleted file mode 100644
index c779437..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_default_user.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_default_user.png
deleted file mode 100644
index 18257e0..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_default_user.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight.png
deleted file mode 100644
index 8ddb375..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index 57a3b99..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png
deleted file mode 100644
index 98ba690..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_default_user.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_default_user.png
deleted file mode 100644
index a35c30d..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_default_user.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight.png
deleted file mode 100644
index 71e1303..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index 1de0a3a..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/heads_up_window_bg.9.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/heads_up_window_bg.9.png
deleted file mode 100644
index b30cf15..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/heads_up_window_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_highlight.png
deleted file mode 100644
index 8014b70..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index 41a34e2..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_highlight.png
deleted file mode 100644
index 9c623e5..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index a011aa1..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/heads_up_window_bg.9.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/heads_up_window_bg.9.png
deleted file mode 100644
index 31eb8f7..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/heads_up_window_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_highlight.png
deleted file mode 100644
index 61a36e3..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index 52bf290..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/heads_up_window_bg.9.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/heads_up_window_bg.9.png
deleted file mode 100644
index c76d0e1..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/heads_up_window_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_highlight.png
deleted file mode 100644
index e5d4273..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index 1cc5009..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png
deleted file mode 100644
index 61947ea..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_lock_open_24dp.png b/packages/SystemUI/res/drawable-xhdpi/ic_lock_open_24dp.png
deleted file mode 100644
index 467d558..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_lock_open_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_default_user.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_default_user.png
deleted file mode 100644
index d14a67f..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_default_user.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight.png
deleted file mode 100644
index c44aafc..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index 05da6da..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png
deleted file mode 100644
index 0b563b1..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_default_user.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_default_user.png
deleted file mode 100644
index 07f16c3..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_default_user.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_highlight.png
deleted file mode 100644
index 0df6203..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index b400b14..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png
deleted file mode 100644
index 3600ee6..0000000
--- a/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_open_24dp.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_open_24dp.png
deleted file mode 100644
index e7d2a9a..0000000
--- a/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_open_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_account_circle.xml b/packages/SystemUI/res/drawable/ic_account_circle.xml
new file mode 100644
index 0000000..a7e8514
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_account_circle.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="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,5.0c1.7,0.0 3.0,1.3 3.0,3.0c0.0,1.7 -1.3,3.0 -3.0,3.0c-1.7,0.0 -3.0,-1.3 -3.0,-3.0C9.0,6.3 10.3,5.0 12.0,5.0zM12.0,19.2c-2.5,0.0 -4.7,-1.3 -6.0,-3.2c0.0,-2.0 4.0,-3.1 6.0,-3.1c2.0,0.0 6.0,1.1 6.0,3.1C16.7,17.9 14.5,19.2 12.0,19.2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_24dp.xml b/packages/SystemUI/res/drawable/ic_lock_24dp.xml
new file mode 100644
index 0000000..b2e486c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_24dp.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="24.0dp"
+ android:height="24.0dp"/>
+
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="@color/keyguard_affordance"
+ android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_open_24dp.xml b/packages/SystemUI/res/drawable/ic_lock_open_24dp.xml
new file mode 100644
index 0000000..28b16dd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_open_24dp.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="24.0dp"
+ android:height="24.0dp"/>
+
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="@color/keyguard_affordance"
+ android:pathData="M12.0,17.0c1.1,0.0 2.0,-0.9 2.0,-2.0s-0.9,-2.0 -2.0,-2.0c-1.1,0.0 -2.0,0.9 -2.0,2.0S10.9,17.0 12.0,17.0zM18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l1.9,0.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM18.0,20.0L6.0,20.0L6.0,10.0l12.0,0.0L18.0,20.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml b/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml
index 3a20c58..787eec5 100644
--- a/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml
@@ -24,5 +24,5 @@
<path
android:fill="#FFFFFFFF"
- android:pathData="M6.6,3.6L5.2,2.2C2.8,4.0 1.2,6.8 1.0,10.0l2.0,0.0C3.2,7.3 4.5,5.0 6.6,3.6zM20.0,10.0l2.0,0.0c-0.2,-3.2 -1.7,-6.0 -4.1,-7.8l-1.4,1.4C18.5,5.0 19.8,7.3 20.0,10.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0l-2.0,-2.0L18.0,10.5zM11.5,22.0c0.1,0.0 0.3,0.0 0.4,0.0c0.7,-0.1 1.2,-0.6 1.4,-1.2c0.1,-0.2 0.2,-0.5 0.2,-0.8l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0z" />
+ android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ringer_audible.xml b/packages/SystemUI/res/drawable/ic_ringer_audible.xml
new file mode 100644
index 0000000..2969948
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ringer_audible.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="32dp"
+ android:height="32dp"/>
+
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ringer_silent.xml b/packages/SystemUI/res/drawable/ic_ringer_silent.xml
new file mode 100644
index 0000000..b5837f6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ringer_silent.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="32dp"
+ android:height="32dp"/>
+
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7C9.5,4.3 9.0,4.5 8.6,4.7l9.4,9.4L18.0,10.5zM17.7,19.0l2.0,2.0l1.3,-1.3L4.3,3.0L3.0,4.3l2.9,2.9C5.3,8.2 5.0,9.3 5.0,10.5L5.0,16.0l-2.0,2.0l0.0,1.0L17.7,19.0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ringer_vibrate.xml b/packages/SystemUI/res/drawable/ic_ringer_vibrate.xml
new file mode 100644
index 0000000..d8ded58
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ringer_vibrate.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="32dp"
+ android:height="32dp"/>
+
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M0.0,15.0l2.0,0.0L2.0,9.0L0.0,9.0L0.0,15.0zM3.0,17.0l2.0,0.0L5.0,7.0L3.0,7.0L3.0,17.0zM22.0,9.0l0.0,6.0l2.0,0.0L24.0,9.0L22.0,9.0zM19.0,17.0l2.0,0.0L21.0,7.0l-2.0,0.0L19.0,17.0zM16.5,3.0l-9.0,0.0C6.7,3.0 6.0,3.7 6.0,4.5l0.0,15.0C6.0,20.3 6.7,21.0 7.5,21.0l9.0,0.0c0.8,0.0 1.5,-0.7 1.5,-1.5l0.0,-15.0C18.0,3.7 17.3,3.0 16.5,3.0zM16.0,19.0L8.0,19.0L8.0,5.0l8.0,0.0L16.0,19.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_vol_zen_off.xml b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
new file mode 100644
index 0000000..ea5ab70
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
@@ -0,0 +1,30 @@
+<!--
+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="32dp"
+ android:height="32dp"/>
+
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="#00000000"
+ android:stroke="#CCCCCC"
+ android:strokeWidth="1.0"
+ android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM4.0,12.0c0.0,-4.4 3.6,-8.0 8.0,-8.0c1.8,0.0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4.0,13.8 4.0,12.0zM12.0,20.0c-1.8,0.0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20.0,10.2 20.0,12.0C20.0,16.4 16.4,20.0 12.0,20.0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_vol_zen_on.xml b/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
new file mode 100644
index 0000000..44024f3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_vol_zen_on.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="32dp"
+ android:height="32dp"/>
+
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM4.0,12.0c0.0,-4.4 3.6,-8.0 8.0,-8.0c1.8,0.0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4.0,13.8 4.0,12.0zM12.0,20.0c-1.8,0.0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20.0,10.2 20.0,12.0C20.0,16.4 16.4,20.0 12.0,20.0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/notification_header_bg.xml b/packages/SystemUI/res/drawable/notification_header_bg.xml
index 09d0d7d..5daec20 100644
--- a/packages/SystemUI/res/drawable/notification_header_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_header_bg.xml
@@ -19,13 +19,11 @@
<item android:state_pressed="true">
<shape>
<solid android:color="@color/background_color_1_press" />
- <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" />
</shape>
</item>
<item>
<shape>
<solid android:color="@color/background_color_1" />
- <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" />
</shape>
</item>
</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_panel_background.xml b/packages/SystemUI/res/drawable/qs_panel_background.xml
index c324976..a1a5362 100644
--- a/packages/SystemUI/res/drawable/qs_panel_background.xml
+++ b/packages/SystemUI/res/drawable/qs_panel_background.xml
@@ -13,11 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetLeft="@dimen/notification_side_padding"
- android:insetRight="@dimen/notification_side_padding">
- <shape>
- <solid android:color="@color/system_primary_color" />
- <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" />
- </shape>
-</inset>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/system_primary_color" />
+ <corners
+ android:radius="@*android:dimen/notification_quantum_rounded_rect_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/ripple_drawable.xml b/packages/SystemUI/res/drawable/ripple_drawable.xml
new file mode 100644
index 0000000..d2bff42
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ripple_drawable.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
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="?android:attr/colorControlHighlight"
+ android:tintMode="src_over"
+ android:pinned="true" />
diff --git a/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml b/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
index 5f12706..84f0950 100644
--- a/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
@@ -55,7 +55,7 @@
systemui:keyCode="4"
android:layout_weight="0"
android:scaleType="center"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_back"
/>
<View
@@ -71,7 +71,7 @@
systemui:keyCode="3"
systemui:keyRepeat="false"
android:layout_weight="0"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_home"
/>
<View
@@ -85,7 +85,7 @@
android:layout_height="match_parent"
android:src="@drawable/ic_sysbar_recent"
android:layout_weight="0"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_recent"
/>
<FrameLayout
@@ -99,7 +99,7 @@
android:contentDescription="@string/accessibility_menu"
android:src="@drawable/ic_sysbar_menu"
android:visibility="invisible"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
systemui:keyCode="82" />
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/ime_switcher"
@@ -109,7 +109,7 @@
android:scaleType="centerInside"
android:src="@drawable/ic_ime_switcher_default"
android:visibility="invisible"
- systemui:glowBackground="@drawable/ic_sysbar_highlight" />
+ android:background="@drawable/ripple_drawable" />
</FrameLayout>
</LinearLayout>
@@ -212,7 +212,7 @@
android:layout_weight="0"
android:visibility="invisible"
android:contentDescription="@string/accessibility_menu"
- systemui:glowBackground="@drawable/ic_sysbar_highlight_land" />
+ android:background="@drawable/ripple_drawable" />
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/ime_switcher"
android:layout_height="@dimen/navigation_extra_key_width"
@@ -221,7 +221,7 @@
android:scaleType="centerInside"
android:src="@drawable/ic_ime_switcher_default"
android:visibility="invisible"
- systemui:glowBackground="@drawable/ic_sysbar_highlight" />
+ android:background="@drawable/ripple_drawable" />
</FrameLayout>
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
android:layout_height="80dp"
@@ -231,7 +231,7 @@
systemui:keyCode="4"
android:layout_weight="0"
android:contentDescription="@string/accessibility_back"
- systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+ android:background="@drawable/ripple_drawable"
/>
<View
android:layout_height="match_parent"
@@ -247,7 +247,7 @@
systemui:keyRepeat="false"
android:layout_weight="0"
android:contentDescription="@string/accessibility_home"
- systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+ android:background="@drawable/ripple_drawable"
/>
<View
android:layout_height="match_parent"
@@ -261,7 +261,7 @@
android:src="@drawable/ic_sysbar_recent_land"
android:layout_weight="0"
android:contentDescription="@string/accessibility_recent"
- systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+ android:background="@drawable/ripple_drawable"
/>
<View
android:layout_height="40dp"
diff --git a/packages/SystemUI/res/layout-sw600dp/heads_up.xml b/packages/SystemUI/res/layout-sw600dp/heads_up.xml
deleted file mode 100644
index f7035fe..0000000
--- a/packages/SystemUI/res/layout-sw600dp/heads_up.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** 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.
-*/
--->
-<com.android.systemui.statusbar.policy.HeadsUpNotificationView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- >
- <FrameLayout
- android:id="@+id/content_holder"
- android:layout_height="wrap_content"
- android:layout_width="@dimen/notification_panel_width"
- android:background="@drawable/heads_up_window_bg"
- />
-</com.android.systemui.statusbar.policy.HeadsUpNotificationView>
diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
index 6a2e3c6..34b674b 100644
--- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
@@ -59,7 +59,7 @@
android:src="@drawable/ic_sysbar_back"
systemui:keyCode="4"
android:layout_weight="0"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_back"
/>
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
@@ -69,7 +69,7 @@
systemui:keyCode="3"
systemui:keyRepeat="true"
android:layout_weight="0"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_home"
/>
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
@@ -77,7 +77,7 @@
android:layout_height="match_parent"
android:src="@drawable/ic_sysbar_recent"
android:layout_weight="0"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_recent"
/>
<Space
@@ -98,7 +98,7 @@
systemui:keyCode="82"
android:visibility="invisible"
android:contentDescription="@string/accessibility_menu"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
/>
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/ime_switcher"
@@ -109,7 +109,7 @@
android:src="@drawable/ic_ime_switcher_default"
android:visibility="invisible"
android:contentDescription="@string/accessibility_ime_switch_button"
- systemui:glowBackground="@drawable/ic_sysbar_highlight" />
+ android:background="@drawable/ripple_drawable" />
</FrameLayout>
</LinearLayout>
@@ -216,7 +216,7 @@
android:src="@drawable/ic_sysbar_back"
systemui:keyCode="4"
android:layout_weight="0"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_back"
/>
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
@@ -226,7 +226,7 @@
systemui:keyCode="3"
systemui:keyRepeat="true"
android:layout_weight="0"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_home"
/>
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
@@ -234,7 +234,7 @@
android:layout_height="match_parent"
android:src="@drawable/ic_sysbar_recent"
android:layout_weight="0"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_recent"
/>
<Space
@@ -255,7 +255,7 @@
systemui:keyCode="82"
android:visibility="invisible"
android:contentDescription="@string/accessibility_menu"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
/>
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/ime_switcher"
@@ -266,7 +266,7 @@
android:visibility="invisible"
android:contentDescription="@string/accessibility_ime_switch_button"
android:scaleType="centerInside"
- systemui:glowBackground="@drawable/ic_sysbar_highlight" />
+ android:background="@drawable/ripple_drawable" />
</FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/heads_up.xml b/packages/SystemUI/res/layout/heads_up.xml
index 7d9cfa1..236fdc3 100644
--- a/packages/SystemUI/res/layout/heads_up.xml
+++ b/packages/SystemUI/res/layout/heads_up.xml
@@ -1,26 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
-**
-** 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.
-*/
+ 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.
-->
<com.android.systemui.statusbar.policy.HeadsUpNotificationView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:id="@+id/content_holder"
- android:background="@drawable/notification_panel_bg"
- />
\ No newline at end of file
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <FrameLayout
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_side_padding"
+ android:layout_marginEnd="@dimen/notification_side_padding"
+ android:elevation="16dp"
+ android:id="@+id/content_holder"
+ style="@style/NotificationsQuickSettings" />
+
+</com.android.systemui.statusbar.policy.HeadsUpNotificationView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 2ec3766..936f73b 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -31,7 +31,6 @@
android:src="@drawable/ic_camera_alt_24dp"
android:scaleType="center"
android:contentDescription="@string/accessibility_camera_button"
- systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
systemui:swipeDirection="start"/>
<com.android.systemui.statusbar.phone.SwipeAffordanceView
@@ -43,7 +42,6 @@
android:src="@drawable/ic_phone_24dp"
android:scaleType="center"
android:contentDescription="@string/accessibility_phone_button"
- systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
systemui:swipeDirection="end"/>
<com.android.systemui.statusbar.phone.KeyguardIndicationTextView
@@ -53,7 +51,8 @@
android:layout_marginBottom="100dp"
android:layout_gravity="bottom|center_horizontal"
android:textStyle="italic"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
+ android:textColor="#ffffff"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
<ImageView
android:id="@+id/lock_icon"
@@ -66,4 +65,4 @@
android:layerType="hardware"
android:tint="#ffffffff"/>
-</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
\ No newline at end of file
+</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index 7470409..fa6f7f5 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -55,7 +55,7 @@
systemui:keyCode="4"
android:layout_weight="0"
android:scaleType="center"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_back"
/>
<View
@@ -71,7 +71,7 @@
systemui:keyCode="3"
systemui:keyRepeat="false"
android:layout_weight="0"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_home"
/>
<View
@@ -85,7 +85,7 @@
android:layout_height="match_parent"
android:src="@drawable/ic_sysbar_recent"
android:layout_weight="0"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_recent"
/>
<FrameLayout
@@ -99,7 +99,7 @@
android:contentDescription="@string/accessibility_menu"
android:src="@drawable/ic_sysbar_menu"
android:visibility="invisible"
- systemui:glowBackground="@drawable/ic_sysbar_highlight"
+ android:background="@drawable/ripple_drawable"
systemui:keyCode="82" />
<com.android.systemui.statusbar.policy.KeyButtonView
@@ -110,7 +110,7 @@
android:scaleType="centerInside"
android:src="@drawable/ic_ime_switcher_default"
android:visibility="invisible"
- systemui:glowBackground="@drawable/ic_sysbar_highlight" />
+ android:background="@drawable/ripple_drawable" />
</FrameLayout>
</LinearLayout>
@@ -219,7 +219,7 @@
android:scaleType="centerInside"
android:src="@drawable/ic_ime_switcher_default"
android:visibility="invisible"
- systemui:glowBackground="@drawable/ic_sysbar_highlight" />
+ android:background="@drawable/ripple_drawable" />
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/menu"
@@ -228,7 +228,7 @@
android:contentDescription="@string/accessibility_menu"
android:src="@drawable/ic_sysbar_menu_land"
android:visibility="invisible"
- systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+ android:background="@drawable/ripple_drawable"
systemui:keyCode="82" />
</FrameLayout>
@@ -238,7 +238,7 @@
android:src="@drawable/ic_sysbar_recent_land"
android:layout_weight="0"
android:contentDescription="@string/accessibility_recent"
- systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+ android:background="@drawable/ripple_drawable"
/>
<View
android:layout_height="match_parent"
@@ -254,7 +254,7 @@
systemui:keyRepeat="false"
android:layout_weight="0"
android:contentDescription="@string/accessibility_home"
- systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+ android:background="@drawable/ripple_drawable"
/>
<View
android:layout_height="match_parent"
@@ -270,7 +270,7 @@
systemui:keyCode="4"
android:layout_weight="0"
android:contentDescription="@string/accessibility_back"
- systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+ android:background="@drawable/ripple_drawable"
/>
<View
android:layout_height="40dp"
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
new file mode 100644
index 0000000..e73b431
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -0,0 +1,57 @@
+<?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"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/system_primary_color" >
+
+ <ImageView
+ android:id="@android:id/button1"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_alignParentStart="true"
+ android:contentDescription="@string/accessibility_quick_settings_close"
+ android:padding="@dimen/qs_panel_padding"
+ android:src="@drawable/ic_qs_close" />
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:layout_alignParentTop="true"
+ android:layout_toEndOf="@android:id/button1"
+ android:layout_toStartOf="@android:id/checkbox"
+ android:gravity="center_vertical"
+ android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+
+ <ImageView
+ android:id="@android:id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:scaleType="fitXY"
+ android:src="?android:attr/dividerHorizontal" />
+
+ <FrameLayout
+ android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@android:id/custom" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 85de645..398787f 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -16,11 +16,10 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/quick_settings_container"
- android:paddingLeft="@dimen/notification_side_padding"
- android:paddingRight="@dimen/notification_side_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/qs_panel_background" >
+ android:background="@drawable/qs_panel_background"
+ android:elevation="2dp">
<com.android.systemui.qs.QSPanel
android:id="@+id/quick_settings_panel"
android:background="#0000"
diff --git a/packages/SystemUI/res/layout/qs_zen_mode_detail.xml b/packages/SystemUI/res/layout/qs_zen_mode_detail.xml
deleted file mode 100644
index 85b294d..0000000
--- a/packages/SystemUI/res/layout/qs_zen_mode_detail.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?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.
--->
-<com.android.systemui.qs.tiles.ZenModeDetail xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/system_secondary_color" >
-
- <ImageView
- android:id="@android:id/button1"
- android:src="@drawable/ic_qs_close"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:layout_alignParentStart="true"
- android:padding="@dimen/qs_panel_padding" />
-
- <Switch
- android:id="@android:id/checkbox"
- android:layout_width="wrap_content"
- android:layout_height="64dp"
- android:layout_alignParentEnd="true"
- android:gravity="center"
- android:padding="@dimen/qs_panel_padding" />
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="match_parent"
- android:layout_height="64dp"
- android:layout_toEndOf="@android:id/button1"
- android:layout_toStartOf="@android:id/checkbox"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
- android:gravity="center_vertical"
- android:paddingStart="@dimen/qs_panel_padding"
- android:text="@string/zen_mode_title" />
-
- <View
- android:id="@android:id/custom"
- android:layout_width="match_parent"
- android:layout_height="2dp"
- android:layout_below="@android:id/title"
- android:background="#888" />
-
- <ListView
- android:id="@android:id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_above="@android:id/button2"
- android:layout_below="@android:id/custom"
- android:divider="#00000000"
- android:dividerHeight="0px" />
-
- <TextView
- android:id="@android:id/button2"
- style="@style/QSBorderless"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
- android:padding="@dimen/qs_panel_padding"
- android:text="@string/quick_settings_more_settings"
- android:textAllCaps="true" />
-
-</com.android.systemui.qs.tiles.ZenModeDetail>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml b/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml
deleted file mode 100644
index fd27aaf..0000000
--- a/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?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"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <RadioButton
- android:id="@android:id/checkbox"
- android:layout_width="32dp"
- android:layout_height="64dp"
- android:layout_alignParentStart="true"
- android:layout_marginStart="@dimen/qs_panel_padding"
- android:gravity="center" />
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="match_parent"
- android:layout_height="64dp"
- android:layout_toEndOf="@android:id/checkbox"
- android:layout_toStartOf="@android:id/button1"
- android:ellipsize="end"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
- android:gravity="center_vertical"
- android:maxLines="1"
- android:text="@string/accessibility_back" />
-
- <ImageView
- android:id="@android:id/button1"
- android:src="@drawable/ic_qs_minus"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:layout_alignParentEnd="true"
- android:layout_marginEnd="48dp"
- android:padding="@dimen/qs_panel_padding"
- android:paddingRight="0px" />
-
- <ImageView
- android:id="@android:id/button2"
- android:src="@drawable/ic_qs_plus"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:layout_alignParentEnd="true"
- android:padding="@dimen/qs_panel_padding" />
-
-</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index 1efda8c..97ed9a0 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -15,7 +15,8 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
- style="@style/BrightnessDialogContainer">
+ style="@style/BrightnessDialogContainer"
+ android:clickable="true">
<ImageView
android:id="@+id/brightness_icon"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 2ec9935..cde83bf 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -59,8 +59,8 @@
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:visibility="invisible"
android:scrollbars="none"
+ android:overScrollMode="never"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
@@ -70,7 +70,9 @@
layout="@layout/qs_panel"
android:layout_marginTop="@dimen/status_bar_header_height_expanded"
android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/notification_side_padding"
+ android:layout_marginRight="@dimen/notification_side_padding"/>
<!-- A view to reserve space for the collapsed stack -->
<View
@@ -79,7 +81,6 @@
</LinearLayout>
</com.android.systemui.statusbar.phone.ObservableScrollView>
-
<com.android.systemui.statusbar.stack.NotificationStackScrollLayout
android:id="@+id/notification_stack_scroller"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 89fa988..7f34041 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -25,7 +25,7 @@
android:paddingStart="@dimen/notification_side_padding"
android:paddingEnd="@dimen/notification_side_padding"
android:baselineAligned="false"
- android:elevation="10dp"
+ android:elevation="4dp"
>
<View
@@ -65,22 +65,13 @@
/>
</RelativeLayout>
- <com.android.keyguard.CarrierText
- android:id="@+id/keyguard_carrier_text"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/status_bar_header_height_keyguard"
- android:layout_marginLeft="8dp"
- android:gravity="center_vertical"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
<com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
android:layout_width="40dp"
android:layout_height="@dimen/status_bar_header_height"
android:layout_alignParentEnd="true"
android:background="@null"
android:scaleType="centerInside"
- android:padding="6dp"
+ android:padding="8dp"
/>
<ImageButton android:id="@+id/settings_button"
@@ -98,6 +89,18 @@
android:layout_marginEnd="4dp"
/>
+ <com.android.keyguard.CarrierText
+ android:id="@+id/keyguard_carrier_text"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/status_bar_header_height_keyguard"
+ android:layout_marginLeft="8dp"
+ android:layout_toStartOf="@id/system_icons_container"
+ android:gravity="center_vertical"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="#ffffff"
+ android:singleLine="true" />
+
<include
layout="@layout/quick_settings_brightness_dialog"
android:id="@+id/brightness_container"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 26616cd..e84300d 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -26,6 +26,10 @@
android:fitsSystemWindows="true"
android:descendantFocusability="afterDescendants">
+ <View android:id="@+id/scrim_behind"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
<include layout="@layout/status_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_height" />
@@ -40,4 +44,8 @@
android:visibility="gone" />
</com.android.systemui.statusbar.phone.PanelHolder>
+ <View android:id="@+id/scrim_in_front"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/SystemUI/res/layout/user_switcher_host.xml b/packages/SystemUI/res/layout/user_switcher_host.xml
index 70c5042..816af57 100644
--- a/packages/SystemUI/res/layout/user_switcher_host.xml
+++ b/packages/SystemUI/res/layout/user_switcher_host.xml
@@ -27,7 +27,7 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="@*android:dimen/volume_panel_top"
+ android:layout_marginTop="@dimen/volume_panel_top"
android:background="@*android:drawable/dialog_full_holo_dark">
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/volume_panel.xml b/packages/SystemUI/res/layout/volume_panel.xml
new file mode 100644
index 0000000..bc7288d
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_panel.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/visible_panel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <FrameLayout
+ android:id="@+id/slider_panel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toLeftOf="@+id/expand_button_divider" />
+
+ <ImageView
+ android:id="@+id/expand_button_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="32dip"
+ android:layout_gravity="top"
+ android:layout_marginBottom="16dip"
+ android:layout_marginTop="16dip"
+ android:layout_toLeftOf="@+id/expand_button"
+ android:scaleType="fitXY"
+ android:src="?android:attr/dividerVertical" />
+
+ <ImageView
+ android:id="@+id/expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_gravity="top"
+ style="@style/BorderlessButton.Tiny"
+ android:padding="16dip" />
+
+ <ImageView
+ android:id="@+id/zen_panel_divider"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/slider_panel"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:scaleType="fitXY"
+ android:src="?android:attr/dividerHorizontal" />
+
+ <ViewStub
+ android:id="@+id/zen_panel_stub"
+ android:layout_below="@+id/zen_panel_divider"
+ android:inflatedId="@+id/zen_panel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout="@layout/zen_mode_panel" />
+
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/volume_panel_item.xml b/packages/SystemUI/res/layout/volume_panel_item.xml
new file mode 100644
index 0000000..98cb8f4
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_panel_item.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="80dip"
+ android:orientation="horizontal"
+ android:layout_marginTop="8dip"
+ android:layout_marginBottom="8dip"
+ android:gravity="start|center_vertical">
+
+ <ImageView
+ android:id="@+id/stream_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dip"
+ android:background="?android:attr/selectableItemBackground"
+ android:contentDescription="@null" />
+
+ <SeekBar
+ style="?android:attr/seekBarStyle"
+ android:id="@+id/seekbar"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:padding="16dip"
+ android:layout_marginEnd="16dip" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/zen_mode_condition.xml b/packages/SystemUI/res/layout/zen_mode_condition.xml
new file mode 100644
index 0000000..8b344000
--- /dev/null
+++ b/packages/SystemUI/res/layout/zen_mode_condition.xml
@@ -0,0 +1,62 @@
+<?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"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ <RadioButton
+ android:id="@android:id/checkbox"
+ android:layout_width="32dp"
+ android:layout_height="@dimen/zen_mode_condition_height"
+ android:layout_alignParentStart="true"
+ android:gravity="center" />
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/zen_mode_condition_height"
+ android:layout_toEndOf="@android:id/checkbox"
+ android:layout_toStartOf="@android:id/button1"
+ android:ellipsize="end"
+ android:gravity="center_vertical"
+ android:maxLines="1"
+ android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
+
+ <ImageView
+ android:id="@android:id/button1"
+ style="@style/BorderlessButton"
+ android:layout_width="@dimen/zen_mode_condition_height"
+ android:layout_height="@dimen/zen_mode_condition_height"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:layout_marginEnd="@dimen/zen_mode_condition_height"
+ android:contentDescription="@string/accessibility_quick_settings_less_time"
+ android:padding="@dimen/zen_mode_condition_detail_button_padding"
+ android:src="@drawable/ic_qs_minus" />
+
+ <ImageView
+ android:id="@android:id/button2"
+ style="@style/BorderlessButton"
+ android:layout_width="@dimen/zen_mode_condition_height"
+ android:layout_height="@dimen/zen_mode_condition_height"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:contentDescription="@string/accessibility_quick_settings_more_time"
+ android:padding="@dimen/zen_mode_condition_detail_button_padding"
+ android:src="@drawable/ic_qs_plus" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
new file mode 100644
index 0000000..ae04bf5
--- /dev/null
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -0,0 +1,51 @@
+<?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.
+-->
+<!-- extends LinearLayout -->
+<com.android.systemui.volume.ZenModePanel xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/zen_mode_panel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/system_primary_color"
+ android:orientation="vertical"
+ android:padding="@dimen/qs_panel_padding" >
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_marginBottom="8dp"
+ android:text="@string/zen_mode_title"
+ android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+
+ <LinearLayout
+ android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" />
+
+ <TextView
+ android:id="@android:id/button2"
+ style="@style/BorderlessButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_gravity="end"
+ android:text="@string/quick_settings_more_settings"
+ android:textAppearance="@style/TextAppearance.QS.DetailButton" />
+
+</com.android.systemui.volume.ZenModePanel>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index d48e627..d26cfae 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -223,8 +223,7 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d more"</item>
</plurals>
- <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
- <skip />
+ <string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Swipe up to unlock"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 23bcea28..c93fbf6 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -225,8 +225,7 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d más"</item>
</plurals>
- <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
- <skip />
+ <string name="speed_bump_explanation" msgid="1288875699658819755">"Notificaciones menos urgentes abajo"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Presionar de nuevo para abrir"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Deslizar el dedo hacia arriba para desbloquear"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 02313b37..98373c9 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -202,15 +202,11 @@
<string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Layar Transmisi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Kecerahan"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OTOMATIS"</string>
- <!-- no translation found for quick_settings_inversion_label (8790919884718619648) -->
- <skip />
+ <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inversi warna"</string>
<string name="quick_settings_color_space_label" msgid="853443689745584770">"Mode koreksi warna"</string>
- <!-- no translation found for quick_settings_more_settings (326112621462813682) -->
- <skip />
- <!-- no translation found for quick_settings_tethering_label (7153452060448575549) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_label (6046917934974004879) -->
- <skip />
+ <string name="quick_settings_more_settings" msgid="326112621462813682">"Setelan lainnya"</string>
+ <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Menambatkan"</string>
+ <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
<string name="recents_empty_message" msgid="2269156590813544104">"TERBARU"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Info Aplikasi"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"telusuri"</string>
@@ -227,8 +223,7 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d lainnya"</item>
</plurals>
- <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
- <skip />
+ <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>
<string name="keyguard_unlock" msgid="8043466894212841998">"Gesek ke atas untuk membuka kunci"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 506ec3d..409b9ba 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -202,7 +202,7 @@
<string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast Screen"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"განათება"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ავტომატურად"</string>
- <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ინვერტირება"</string>
+ <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ფერების შებრუნება"</string>
<string name="quick_settings_color_space_label" msgid="853443689745584770">"ფერთა კორექციის რეჟიმი"</string>
<string name="quick_settings_more_settings" msgid="326112621462813682">"დამატებითი პარამეტრები"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"მოდემის რეჟიმი"</string>
@@ -223,8 +223,7 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d სხვა"</item>
</plurals>
- <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
- <skip />
+ <string name="speed_bump_explanation" msgid="1288875699658819755">"ქვემოთ მითითებულია ნაკლებად სასწრაფო შეტყობინებები"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"შეეხეთ ისევ გასახსნელად"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"გაასრიალეთ ზევით განსაბლოკად"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 09e52cc..82216de1 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -223,8 +223,7 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d lagi"</item>
</plurals>
- <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
- <skip />
+ <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>
<string name="keyguard_unlock" msgid="8043466894212841998">"Leret ke atas untuk membuka kunci"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 079c8f0..b0e58e4 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -204,15 +204,11 @@
<string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Transmitir tela"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brilho"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
- <!-- no translation found for quick_settings_inversion_label (8790919884718619648) -->
- <skip />
+ <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverter cores"</string>
<string name="quick_settings_color_space_label" msgid="853443689745584770">"Modo de correção de cor"</string>
- <!-- no translation found for quick_settings_more_settings (326112621462813682) -->
- <skip />
- <!-- no translation found for quick_settings_tethering_label (7153452060448575549) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_label (6046917934974004879) -->
- <skip />
+ <string name="quick_settings_more_settings" msgid="326112621462813682">"Mais configurações"</string>
+ <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
+ <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Ponto de acesso"</string>
<string name="recents_empty_message" msgid="2269156590813544104">"RECENTES"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações do aplicativo"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
@@ -229,8 +225,7 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Mais %d"</item>
</plurals>
- <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
- <skip />
+ <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>
<string name="keyguard_unlock" msgid="8043466894212841998">"Deslize para cima para desbloquear"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 10afe88..c48733f 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -223,8 +223,7 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Încă %d"</item>
</plurals>
- <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
- <skip />
+ <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>
<string name="keyguard_unlock" msgid="8043466894212841998">"Glisați în sus pentru a debloca"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 1d27854..3525b5e 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -221,7 +221,7 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d zaidi"</item>
</plurals>
- <string name="speed_bump_explanation" msgid="1288875699658819755">"Arifa zisizokuwa za dharura sana hapo chini"</string>
+ <string name="speed_bump_explanation" msgid="1288875699658819755">"Arifa zisizo za dharura sana ziko hapo chini"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Gonga tena ili ufungue"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Telezesha kidole ili ufungue"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 22815f3..5750faa 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -57,6 +57,6 @@
<!-- The margin between the clock and the notifications on Keyguard. See
keyguard_clock_height_fraction_* for the difference between min and max.-->
- <dimen name="keyguard_clock_notifications_margin_min">32dp</dimen>
- <dimen name="keyguard_clock_notifications_margin_max">32dp</dimen>
+ <dimen name="keyguard_clock_notifications_margin_min">36dp</dimen>
+ <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 306416b..3217daa 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -223,7 +223,7 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"อีก %d"</item>
</plurals>
- <string name="speed_bump_explanation" msgid="1288875699658819755">"การแจ้งเตือนที่เร่งด่วนน้อยทางด้านล่าง"</string>
+ <string name="speed_bump_explanation" msgid="1288875699658819755">"การแจ้งเตือนที่เร่งด่วนน้อยด้านล่าง"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"แตะอีกครั้งเพื่อเปิด"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"กวาดขึ้นเพื่อปลดล็อก"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index debef15..2f8ae2f 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -202,15 +202,11 @@
<string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"I-cast ang Screen"</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>
- <!-- no translation found for quick_settings_inversion_label (8790919884718619648) -->
- <skip />
+ <string name="quick_settings_inversion_label" msgid="8790919884718619648">"I-invert ang mga kulay"</string>
<string name="quick_settings_color_space_label" msgid="853443689745584770">"Mode ng pagtatama ng kulay"</string>
- <!-- no translation found for quick_settings_more_settings (326112621462813682) -->
- <skip />
- <!-- no translation found for quick_settings_tethering_label (7153452060448575549) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_label (6046917934974004879) -->
- <skip />
+ <string name="quick_settings_more_settings" msgid="326112621462813682">"Marami pang setting"</string>
+ <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Nagte-tether"</string>
+ <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
<string name="recents_empty_message" msgid="2269156590813544104">"MGA KAMAKAILAN"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Impormasyon ng Application"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"maghanap"</string>
@@ -227,8 +223,7 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d pa"</item>
</plurals>
- <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
- <skip />
+ <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>
<string name="keyguard_unlock" msgid="8043466894212841998">"Mag-swipe pataas upang i-unlock"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index c6da6ac..d8e1c35 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -225,8 +225,7 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"还有%d条"</item>
</plurals>
- <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
- <skip />
+ <string name="speed_bump_explanation" msgid="1288875699658819755">"不太紧急的通知会显示在下方"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"再次点按即可打开"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"向上滑动即可解锁"</string>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 8fd1206..3549689 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -20,8 +20,6 @@
<attr name="keyCode" format="integer" />
<!-- does this button generate longpress / repeat events? -->
<attr name="keyRepeat" format="boolean" />
- <!-- drawable to use for a swelling, glowing background on press -->
- <attr name="glowBackground" format="reference" />
</declare-styleable>
<declare-styleable name="ToggleSlider">
<attr name="text" format="string" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index e5ed3d6..4e37dbb 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -31,8 +31,6 @@
<drawable name="recents_callout_line">#99ffffff</drawable>
<drawable name="notification_item_background_legacy_color">#ffaaaaaa</drawable>
<drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable>
- <color name="notification_panel_scrim_color">#A0000000</color>
- <color name="notification_panel_scrim_color_keyguard">#80000000</color>
<color name="batterymeter_frame_color">#66FFFFFF</color><!-- 40% white -->
<color name="batterymeter_charge_color">#FFFFFFFF</color>
<color name="batterymeter_bolt_color">#FFFFFFFF</color>
@@ -75,6 +73,8 @@
<!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
<color name="recents_task_bar_dark_dismiss_color">#ff333333</color>
+ <color name="keyguard_affordance">#ffffffff</color>
+
<!-- Our quantum color palette (deep teal) -->
<color name="primary_color">#ff7fcac3</color>
<color name="background_color_1">#ff384248</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 0bd4f18..6405ae6 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -135,5 +135,8 @@
<!-- Defines the implementation of the velocity tracker to be used for the panel expansion. Can
be 'platform' or 'noisy' (i.e. for noisy touch screens). -->
<string name="velocity_tracker_impl" translatable="false">platform</string>
+
+ <!-- Wait on the touch feedback this long before performing an action. -->
+ <integer name="feedback_start_delay">300</integer>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c209434..6e35230 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -196,6 +196,12 @@
<dimen name="qs_dual_tile_height">109dp</dimen>
<dimen name="qs_dual_tile_padding">12dp</dimen>
+ <!-- How far the expanded QS panel peeks from the header in collapsed state. -->
+ <dimen name="qs_peek_height">8dp</dimen>
+
+ <dimen name="zen_mode_condition_detail_button_padding">8dp</dimen>
+ <dimen name="zen_mode_condition_height">48dp</dimen>
+
<!-- used by DessertCase -->
<dimen name="dessert_case_cell_size">192dp</dimen>
@@ -246,7 +252,7 @@
<dimen name="notification_side_padding">8dp</dimen>
<!-- Z distance between notifications if they are in the stack -->
- <dimen name="z_distance_between_notifications">2dp</dimen>
+ <dimen name="z_distance_between_notifications">1dp</dimen>
<!-- The padding between the individual notification cards when dimmed. -->
<dimen name="notification_padding_dimmed">0dp</dimen>
@@ -288,4 +294,9 @@
<dimen name="keyguard_clock_notifications_margin_min">22dp</dimen>
<dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
+ <!-- Volume panel dialog y offset -->
+ <dimen name="volume_panel_top">16dp</dimen>
+
+ <!-- Volume panel dialog width -->
+ <dimen name="volume_panel_width">300dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f5bc353..ef3956e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -388,6 +388,12 @@
<string name="accessibility_quick_settings_location">Location <xliff:g id="state" example="Off">%s</xliff:g>.</string>
<!-- Content description of the alarm tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_alarm">Alarm set for <xliff:g id="time" example="Wed 3:30 PM">%s</xliff:g>.</string>
+ <!-- Content description of quick settings detail panel close button (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_close">Close panel</string>
+ <!-- Content description of zen mode time condition plus button (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_more_time">More time</string>
+ <!-- Content description of zen mode time condition minus button (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_less_time">Less time</string>
<!-- Title of dialog shown when 2G-3G data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
<string name="data_usage_disabled_dialog_3g_title">2G-3G data disabled</string>
@@ -512,6 +518,8 @@
<string name="quick_settings_tethering_label">Tethering</string>
<!-- QuickSettings: Hotspot. [CHAR LIMIT=NONE] -->
<string name="quick_settings_hotspot_label">Hotspot</string>
+ <!-- QuickSettings: Notifications [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_notifications_label">Notifications</string>
<!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
<string name="recents_empty_message">RECENTS</string>
@@ -563,4 +571,19 @@
<string name="keyguard_unlock">Swipe up to unlock</string>
<string name="bugreport_tile_extended" translatable="false">%s\n%s (%s)</string>
+
+ <!-- Zen mode condition: no exit criteria. [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_forever">Until you turn this off</string>
+
+ <!-- Zen mode condition: time duration in minutes. [CHAR LIMIT=NONE] -->
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one">For one minute</item>
+ <item quantity="other">For %d minutes</item>
+ </plurals>
+
+ <!-- Zen mode condition: time duration in hours. [CHAR LIMIT=NONE] -->
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one">For one hour</item>
+ <item quantity="other">For %d hours</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 19888a8..6a12232 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -133,6 +133,32 @@
<item name="android:fadingEdge">horizontal</item>
</style>
+ <style name="TextAppearance.QS">
+ <item name="android:textStyle">normal</item>
+ <item name="android:textColor">#ffffff</item>
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="TextAppearance.QS.DetailHeader">
+ <item name="android:textSize">20sp</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
+ </style>
+
+ <style name="TextAppearance.QS.DetailItemPrimary">
+ <item name="android:textSize">16sp</item>
+ </style>
+
+ <style name="TextAppearance.QS.DetailItemSecondary">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">#7fcac3</item>
+ </style>
+
+ <style name="TextAppearance.QS.DetailButton">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
+ </style>
+
<style name="BaseBrightnessDialogContainer">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
@@ -192,9 +218,9 @@
<item name="android:colorControlActivated">@color/system_accent_color</item>
</style>
- <style name="QSBorderless" parent="@android:style/Widget.Quantum.Button.Borderless" />
+ <style name="BorderlessButton" parent="@android:style/Widget.Quantum.Button.Borderless" />
- <style name="QSBorderless.Tiny">
+ <style name="BorderlessButton.Tiny">
<item name="android:minHeight">12dip</item>
<item name="android:minWidth">12dip</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 6387a92..1b12cb0 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -56,6 +56,7 @@
static final float ALPHA_FADE_END = 0.5f; // fraction of thumbnail width
// beyond which alpha->0
private float mMinAlpha = 0f;
+ private float mMaxAlpha = 1f;
private float mPagingTouchSlop;
private Callback mCallback;
@@ -140,6 +141,10 @@
mMinAlpha = minAlpha;
}
+ public void setMaxAlpha(float maxAlpha) {
+ mMaxAlpha = maxAlpha;
+ }
+
private float getAlphaForOffset(View view) {
float viewSize = getSize(view);
final float fadeSize = ALPHA_FADE_END * viewSize;
@@ -150,7 +155,7 @@
} else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) {
result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
}
- return Math.max(mMinAlpha, result);
+ return Math.min(Math.max(mMinAlpha, result), mMaxAlpha);
}
private void updateAlphaFromOffset(View animView, boolean dismissable) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index d7ce255..630ba13 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -42,12 +42,12 @@
private final Class<?>[] SERVICES = new Class[] {
com.android.systemui.keyguard.KeyguardViewMediator.class,
com.android.systemui.recent.Recents.class,
+ com.android.systemui.volume.VolumeUI.class,
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.volume.VolumeUI.class,
};
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e73e904..b2872fa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -62,6 +62,7 @@
import com.android.keyguard.analytics.Session;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarWindowManager;
@@ -1316,9 +1317,10 @@
}
public StatusBarKeyguardViewManager registerStatusBar(PhoneStatusBar phoneStatusBar,
- ViewGroup container, StatusBarWindowManager statusBarWindowManager) {
+ ViewGroup container, StatusBarWindowManager statusBarWindowManager,
+ ScrimController scrimController) {
mStatusBarKeyguardViewManager.registerStatusBar(phoneStatusBar, container,
- statusBarWindowManager);
+ statusBarWindowManager, scrimController);
return mStatusBarKeyguardViewManager;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 835a5c4..c76ee8c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -26,6 +26,7 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.systemui.R;
import com.android.systemui.qs.QSTile.State;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
@@ -35,6 +36,7 @@
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.TetheringController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.volume.VolumeComponent;
import java.util.List;
import java.util.Objects;
@@ -49,12 +51,12 @@
public abstract class QSTile<TState extends State> implements Listenable {
protected final String TAG = "QSTile." + getClass().getSimpleName();
protected static final boolean DEBUG = false;
- public static final int FEEDBACK_START_DELAY = 400;
protected final Host mHost;
protected final Context mContext;
protected final H mHandler;
protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
+ private final int mFeedbackStartDelay;
private Callback mCallback;
protected final TState mState = newTileState();
@@ -68,6 +70,7 @@
mHost = host;
mContext = host.getContext();
mHandler = new H(host.getLooper());
+ mFeedbackStartDelay = mContext.getResources().getInteger(R.integer.feedback_start_delay);
}
public boolean supportsDualTargets() {
@@ -116,6 +119,10 @@
mHandler.obtainMessage(H.USER_SWITCH, newUserId).sendToTarget();
}
+ protected void postAfterFeedback(Runnable runnable) {
+ mHandler.postDelayed(runnable, mFeedbackStartDelay);
+ }
+
// call only on tile worker looper
private void handleSetCallback(Callback callback) {
@@ -213,6 +220,7 @@
ZenModeController getZenModeController();
TetheringController getTetheringController();
CastController getCastController();
+ VolumeComponent getVolumeComponent();
}
public static class State {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 5eecc20..2edd8d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -84,7 +84,8 @@
removeView(mLabel);
}
final Resources res = mContext.getResources();
- mLabel = new TextView(mDual ? new ContextThemeWrapper(mContext, R.style.QSBorderless_Tiny)
+ mLabel = new TextView(mDual
+ ? new ContextThemeWrapper(mContext, R.style.BorderlessButton_Tiny)
: mContext);
mLabel.setId(android.R.id.title);
mLabel.setTextColor(res.getColor(R.color.qs_tile_text));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index c0f9bf2..191bac9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.ConnectivityManager;
import android.provider.Settings.Global;
import com.android.systemui.R;
@@ -52,10 +53,9 @@
}
private void setEnabled(boolean enabled) {
- mSetting.setValue(enabled ? 1 : 0);
- final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- intent.putExtra("state", enabled);
- mContext.sendBroadcast(intent);
+ final ConnectivityManager mgr =
+ (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mgr.setAirplaneMode(enabled);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
index fa41837..07ea825 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
@@ -58,13 +58,13 @@
@Override
protected void handleClick() {
- mHandler.postDelayed(new Runnable() {
+ postAfterFeedback(new Runnable() {
@Override
public void run() {
mHost.collapsePanels();
mUiHandler.post(mShowDialog);
}
- }, FEEDBACK_START_DELAY);
+ });
}
@Override
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 907c77e..6793051 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -65,12 +65,12 @@
@Override
protected void handleClick() {
- mHandler.postDelayed(new Runnable() {
+ postAfterFeedback(new Runnable() {
public void run() {
mHost.collapsePanels();
mUiHandler.post(mShowDialog);
}
- }, FEEDBACK_START_DELAY);
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
new file mode 100644
index 0000000..130f9ce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.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.qs.tiles;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.volume.VolumeComponent;
+import com.android.systemui.volume.VolumePanel;
+import com.android.systemui.volume.ZenModePanel;
+
+/** Quick settings tile: Notifications **/
+public class NotificationsTile extends QSTile<NotificationsTile.NotificationsState> {
+ private final ZenModeController mZenController;
+ private final AudioManager mAudioManager;
+
+ public NotificationsTile(Host host) {
+ super(host);
+ mZenController = host.getZenModeController();
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ }
+
+ @Override
+ public View createDetailView(Context context, ViewGroup root) {
+ final Context themedContext = new ContextThemeWrapper(mContext, R.style.QSAccentTheme);
+ final View v = LayoutInflater.from(themedContext).inflate(R.layout.qs_detail, root, false);
+ final TextView title = (TextView) v.findViewById(android.R.id.title);
+ title.setText(R.string.quick_settings_notifications_label);
+ final View close = v.findViewById(android.R.id.button1);
+ close.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showDetail(false);
+ }
+ });
+ final ViewGroup content = (ViewGroup) v.findViewById(android.R.id.content);
+ final VolumeComponent volumeComponent = mHost.getVolumeComponent();
+ final VolumePanel vp = new VolumePanel(mContext, content, mZenController);
+ v.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ volumeComponent.setVolumePanel(null);
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ volumeComponent.setVolumePanel(vp);
+ }
+ });
+ vp.setZenModePanelCallback(new ZenModePanel.Callback() {
+ @Override
+ public void onMoreSettings() {
+ mHost.startSettingsActivity(ZenModePanel.ZEN_SETTINGS);
+ }
+
+ @Override
+ public void onInteraction() {
+ // noop
+ }
+ });
+ vp.postVolumeChanged(AudioManager.STREAM_NOTIFICATION, AudioManager.FLAG_SHOW_UI);
+ return v;
+ }
+
+ @Override
+ protected NotificationsState newTileState() {
+ return new NotificationsState();
+ }
+
+ @Override
+ public void setListening(boolean listening) {
+ if (listening) {
+ mZenController.addCallback(mCallback);
+ final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
+ mContext.registerReceiver(mReceiver, filter);
+ } else {
+ mZenController.removeCallback(mCallback);
+ mContext.unregisterReceiver(mReceiver);
+ }
+ }
+
+ @Override
+ protected void handleClick() {
+ if (mState.zen) {
+ mZenController.setZen(false);
+ } else {
+ showDetail(true);
+ }
+ }
+
+ @Override
+ protected void handleUpdateState(NotificationsState state, Object arg) {
+ state.visible = true;
+ state.zen = arg instanceof Boolean ? (Boolean) arg : mZenController.isZen();
+ state.ringerMode = mAudioManager.getRingerMode();
+ if (state.zen) {
+ state.iconId = R.drawable.ic_qs_zen_on;
+ } else if (state.ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+ state.iconId = R.drawable.ic_qs_ringer_vibrate;
+ } else if (state.ringerMode == AudioManager.RINGER_MODE_SILENT) {
+ state.iconId = R.drawable.ic_qs_ringer_silent;
+ } else {
+ state.iconId = R.drawable.ic_qs_ringer_audible;
+ }
+ state.label = mContext.getString(R.string.quick_settings_notifications_label);
+ }
+
+ private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
+ @Override
+ public void onZenChanged(boolean zen) {
+ if (DEBUG) Log.d(TAG, "onZenChanged " + zen);
+ refreshState(zen);
+ }
+ };
+
+ public static final class NotificationsState extends QSTile.State {
+ public boolean zen;
+ public int ringerMode;
+
+ @Override
+ public boolean copyTo(State other) {
+ final NotificationsState o = (NotificationsState) other;
+ final boolean changed = o.zen != zen || o.ringerMode != ringerMode;
+ o.zen = zen;
+ o.ringerMode = ringerMode;
+ return super.copyTo(other) || changed;
+ }
+
+ @Override
+ protected StringBuilder toStringBuilder() {
+ final StringBuilder rt = super.toStringBuilder();
+ rt.insert(rt.length() - 1, ",zen=" + zen + ",ringerMode=" + ringerMode);
+ return rt;
+ }
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
+ refreshState();
+ }
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java
deleted file mode 100644
index c5e9b52..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java
+++ /dev/null
@@ -1,108 +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.qs.tiles;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioManager;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-
-/** Quick settings tile: Ringer mode **/
-public class RingerModeTile extends QSTile<RingerModeTile.IntState> {
-
- private final AudioManager mAudioManager;
-
- public RingerModeTile(Host host) {
- super(host);
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- }
-
- @Override
- protected IntState newTileState() {
- return new IntState();
- }
-
- @Override
- public void setListening(boolean listening) {
- if (listening) {
- final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
- mContext.registerReceiver(mReceiver, filter);
- } else {
- mContext.unregisterReceiver(mReceiver);
- }
- }
-
- @Override
- protected void handleClick() {
- final int oldValue = (Integer) mState.value;
- final int newValue =
- oldValue == AudioManager.RINGER_MODE_NORMAL ? AudioManager.RINGER_MODE_VIBRATE
- : oldValue == AudioManager.RINGER_MODE_VIBRATE ? AudioManager.RINGER_MODE_SILENT
- : AudioManager.RINGER_MODE_NORMAL;
-
- mAudioManager.setRingerMode(newValue);
- }
-
- @Override
- protected void handleUpdateState(IntState state, Object arg) {
- final int ringerMode = mAudioManager.getRingerMode();
- state.visible = true;
- state.value = ringerMode;
- if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
- state.iconId = R.drawable.ic_qs_ringer_vibrate;
- state.label = "Vibrate";
- } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
- state.iconId = R.drawable.ic_qs_ringer_silent;
- state.label = "Silent";
- } else {
- state.iconId = R.drawable.ic_qs_ringer_audible;
- state.label = "Audible";
- }
- }
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
- refreshState();
- }
- }
- };
-
- public static class IntState extends QSTile.State {
- public int value;
-
- @Override
- public boolean copyTo(State other) {
- final IntState o = (IntState) other;
- final boolean changed = o.value != value;
- o.value = value;
- return super.copyTo(other) || changed;
- }
-
- @Override
- protected StringBuilder toStringBuilder() {
- final StringBuilder rt = super.toStringBuilder();
- rt.insert(rt.length() - 1, ",value=" + value);
- return rt;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java
deleted file mode 100644
index f30f791..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java
+++ /dev/null
@@ -1,273 +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.qs.tiles;
-
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings;
-import android.service.notification.Condition;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.RadioButton;
-import android.widget.RelativeLayout;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import java.util.HashSet;
-
-/** Quick settings control panel: Zen mode **/
-public class ZenModeDetail extends RelativeLayout {
- private static final String TAG = "ZenModeDetail";
- private static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
- private static final int[] MINUTES = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
-
- private final H mHandler = new H();
-
- private int mMinutesIndex = 3;
- private Context mContext;
- private ZenModeTile mTile;
- private QSTile.Host mHost;
- private ZenModeController mController;
-
- private Switch mSwitch;
- private ConditionAdapter mAdapter;
-
- public ZenModeDetail(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void init(ZenModeTile tile) {
- mTile = tile;
- mHost = mTile.getHost();
- mContext = getContext();
- mController = mHost.getZenModeController();
-
- final ImageView close = (ImageView) findViewById(android.R.id.button1);
- close.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mTile.showDetail(false);
- }
- });
- mSwitch = (Switch) findViewById(android.R.id.checkbox);
- mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- mController.setZen(isChecked);
- }
- });
- mSwitch.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- final boolean isChecked = mSwitch.isChecked();
- mController.setZen(isChecked);
- if (!isChecked) {
- mTile.showDetail(false);
- }
- }
- });
-
- final View moreSettings = findViewById(android.R.id.button2);
- moreSettings.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mHost.startSettingsActivity(ZEN_SETTINGS);
- mTile.showDetail(false);
- }
- });
- final ListView conditions = (ListView) findViewById(android.R.id.content);
- mAdapter = new ConditionAdapter(mContext);
- conditions.setAdapter(mAdapter);
- mAdapter.add(updateTimeCondition());
-
- updateZen(mController.isZen());
- }
-
- private Condition updateTimeCondition() {
- final int minutes = MINUTES[mMinutesIndex];
- final long millis = System.currentTimeMillis() + minutes * 60 * 1000;
- final Uri id = new Uri.Builder().scheme(Condition.SCHEME).authority("android")
- .appendPath("countdown").appendPath(Long.toString(millis)).build();
- final int num = minutes < 60 ? minutes : minutes / 60;
- final String units = minutes < 60 ? "minutes" : minutes == 60 ? "hour" : "hours";
- return new Condition(id, "For " + num + " " + units, "", "", 0, Condition.STATE_TRUE,
- Condition.FLAG_RELEVANT_NOW);
- }
-
- private void editTimeCondition(int delta) {
- final int i = mMinutesIndex + delta;
- if (i < 0 || i >= MINUTES.length) return;
- mMinutesIndex = i;
- mAdapter.remove(mAdapter.getItem(0));
- final Condition c = updateTimeCondition();
- mAdapter.insert(c, 0);
- select(c);
- }
-
- private void select(Condition condition) {
- mController.select(condition);
- }
-
- private void updateZen(boolean zen) {
- mHandler.obtainMessage(H.UPDATE_ZEN, zen ? 1 : 0, 0).sendToTarget();
- }
-
- private void updateConditions(Condition[] conditions) {
- if (conditions == null) return;
- mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
- }
-
- private void handleUpdateZen(boolean zen) {
- mSwitch.setChecked(zen);
- }
-
- private void handleUpdateConditions(Condition[] conditions) {
- for (int i = mAdapter.getCount() - 1; i > 0; i--) {
- mAdapter.remove(mAdapter.getItem(i));
- }
- for (Condition condition : conditions) {
- mAdapter.add(condition);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mController.addCallback(mCallback);
- mController.requestConditions(true);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mController.removeCallback(mCallback);
- mController.requestConditions(false);
- }
-
- private final class H extends Handler {
- private static final int UPDATE_ZEN = 1;
- private static final int UPDATE_CONDITIONS = 2;
-
- public H() {
- super(Looper.getMainLooper());
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == UPDATE_ZEN) {
- handleUpdateZen(msg.arg1 == 1);
- } else if (msg.what == UPDATE_CONDITIONS) {
- handleUpdateConditions((Condition[])msg.obj);
- }
- }
- }
-
- private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
- @Override
- public void onZenChanged(boolean zen) {
- updateZen(zen);
- }
- public void onConditionsChanged(Condition[] conditions) {
- updateConditions(conditions);
- }
- };
-
- private final class ConditionAdapter extends ArrayAdapter<Condition> {
- private final LayoutInflater mInflater;
- private final HashSet<RadioButton> mRadioButtons = new HashSet<RadioButton>();
-
- public ConditionAdapter(Context context) {
- super(context, 0);
- mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme));
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final Condition condition = getItem(position);
- final boolean enabled = condition.state == Condition.STATE_TRUE;
-
- final View row = convertView != null ? convertView : mInflater
- .inflate(R.layout.qs_zen_mode_detail_condition, parent, false);
- final RadioButton rb = (RadioButton) row.findViewById(android.R.id.checkbox);
- mRadioButtons.add(rb);
- rb.setEnabled(enabled);
- rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (isChecked) {
- for (RadioButton otherButton : mRadioButtons) {
- if (otherButton == rb) continue;
- otherButton.setChecked(false);
- }
- select(condition);
- }
- }
- });
- final TextView title = (TextView) row.findViewById(android.R.id.title);
- title.setText(condition.summary);
- title.setEnabled(enabled);
- title.setAlpha(enabled ? 1 : .5f);
- final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
- button1.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- rb.setChecked(true);
- editTimeCondition(-1);
- }
- });
-
- final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2);
- button2.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- rb.setChecked(true);
- editTimeCondition(1);
- }
- });
- title.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- rb.setChecked(true);
- }
- });
- if (position != 0) {
- button1.setVisibility(View.GONE);
- button2.setVisibility(View.GONE);
- }
- if (position == 0 && mRadioButtons.size() == 1) {
- rb.setChecked(true);
- }
- return row;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java
deleted file mode 100644
index bfa9c19..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java
+++ /dev/null
@@ -1,87 +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.qs.tiles;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-/** Quick settings tile: Zen mode **/
-public class ZenModeTile extends QSTile<QSTile.BooleanState> {
- private final ZenModeController mController;
-
- public ZenModeTile(Host host) {
- super(host);
- mController = host.getZenModeController();
- }
-
- @Override
- public View createDetailView(Context context, ViewGroup root) {
- final Context themedContext = new ContextThemeWrapper(mContext, R.style.QSAccentTheme);
- final ZenModeDetail v = (ZenModeDetail) LayoutInflater.from(themedContext)
- .inflate(R.layout.qs_zen_mode_detail, root, false);
- v.init(this);
- return v;
- }
-
- @Override
- protected BooleanState newTileState() {
- return new BooleanState();
- }
-
- @Override
- public void setListening(boolean listening) {
- if (listening) {
- mController.addCallback(mCallback);
- } else {
- mController.removeCallback(mCallback);
- }
- }
-
- @Override
- protected void handleClick() {
- final boolean newZen = !mState.value;
- mController.setZen(newZen);
- if (newZen) {
- showDetail(true);
- }
- }
-
- @Override
- protected void handleUpdateState(BooleanState state, Object arg) {
- final boolean zen = arg instanceof Boolean ? (Boolean)arg : mController.isZen();
- state.value = zen;
- state.visible = true;
- state.iconId = zen ? R.drawable.ic_qs_zen_on : R.drawable.ic_qs_zen_off;
- state.label = mContext.getString(R.string.zen_mode_title);
- }
-
- private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
- @Override
- public void onZenChanged(boolean zen) {
- if (DEBUG) Log.d(TAG, "onZenChanged " + zen);
- refreshState(zen);
- }
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index 27881c4..65e1cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -72,8 +72,7 @@
window.setGravity(Gravity.TOP);
WindowManager.LayoutParams lp = window.getAttributes();
// Offset from the top
- lp.y = getContext().getResources().getDimensionPixelOffset(
- com.android.internal.R.dimen.volume_panel_top);
+ lp.y = getContext().getResources().getDimensionPixelOffset(R.dimen.volume_panel_top);
lp.type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
lp.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index ac16164..dcd187c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -21,15 +21,25 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.RectF;
+import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;
-
import com.android.systemui.R;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
/**
* Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -41,6 +51,36 @@
private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
private static final int ACTIVATE_ANIMATION_LENGTH = 220;
+ /**
+ * The amount of width, which is kept in the end when performing a disappear animation (also
+ * the amount from which the horizontal appearing begins)
+ */
+ private static final float HORIZONTAL_COLLAPSED_REST_PARTIAL = 0.05f;
+
+ /**
+ * At which point from [0,1] does the horizontal collapse animation end (or start when
+ * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
+ */
+ private static final float HORIZONTAL_ANIMATION_END = 0.2f;
+
+ /**
+ * At which point from [0,1] does the alpha animation end (or start when
+ * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
+ */
+ private static final float ALPHA_ANIMATION_END = 0.0f;
+
+ /**
+ * At which point from [0,1] does the horizontal collapse animation start (or start when
+ * expanding)? 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
+ */
+ private static final float HORIZONTAL_ANIMATION_START = 1.0f;
+
+ /**
+ * At which point from [0,1] does the vertical collapse animation start (or end when
+ * expanding) 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
+ */
+ private static final float VERTICAL_ANIMATION_START = 1.0f;
+
private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
= new PathInterpolator(0.6f, 0, 0.5f, 1);
private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
@@ -53,6 +93,7 @@
private int mBgTint = 0;
private int mDimmedBgTint = 0;
+ private final int mRoundedRectCornerRadius;
/**
* Flag to indicate that the notification has been touched once and the second touch will
@@ -66,22 +107,41 @@
private OnActivatedListener mOnActivatedListener;
- private Interpolator mLinearOutSlowInInterpolator;
- private Interpolator mFastOutSlowInInterpolator;
+ private final Interpolator mLinearOutSlowInInterpolator;
+ private final Interpolator mFastOutSlowInInterpolator;
+ private final Interpolator mSlowOutFastInInterpolator;
+ private final Interpolator mSlowOutLinearInInterpolator;
+ private final Interpolator mLinearInterpolator;
+ private Interpolator mCurrentAppearInterpolator;
+ private Interpolator mCurrentAlphaInterpolator;
private NotificationBackgroundView mBackgroundNormal;
private NotificationBackgroundView mBackgroundDimmed;
private ObjectAnimator mBackgroundAnimator;
+ private RectF mAppearAnimationRect = new RectF();
+ private PorterDuffColorFilter mAppearAnimationFilter;
+ private float mAnimationTranslationY;
+ private boolean mDrawingAppearAnimation;
+ private Paint mAppearPaint = new Paint();
+ private ValueAnimator mAppearAnimator;
+ private float mAppearAnimationFraction = -1.0f;
+ private float mAppearAnimationTranslation;
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mFastOutSlowInInterpolator =
AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
+ mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
mLinearOutSlowInInterpolator =
AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
+ mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
+ mLinearInterpolator = new LinearInterpolator();
setClipChildren(false);
setClipToPadding(false);
+ mAppearAnimationFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
+ mRoundedRectCornerRadius = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
}
@Override
@@ -89,6 +149,7 @@
super.onFinishInflate();
mBackgroundNormal = (NotificationBackgroundView) findViewById(R.id.backgroundNormal);
mBackgroundDimmed = (NotificationBackgroundView) findViewById(R.id.backgroundDimmed);
+ updateBackground();
updateBackgroundResources();
}
@@ -316,6 +377,202 @@
mBackgroundDimmed.setClipTopAmount(clipTopAmount);
}
+ @Override
+ public void performRemoveAnimation(float translationDirection, Runnable onFinishedRunnable) {
+ enableAppearDrawing(true);
+ if (mDrawingAppearAnimation) {
+ startAppearAnimation(false /* isAppearing */, translationDirection,
+ 0, onFinishedRunnable);
+ }
+ }
+
+ @Override
+ public void performAddAnimation(long delay) {
+ enableAppearDrawing(true);
+ if (mDrawingAppearAnimation) {
+ startAppearAnimation(true /* isAppearing */, -1.0f, delay, null);
+ }
+ }
+
+ private void startAppearAnimation(boolean isAppearing,
+ float translationDirection, long delay, final Runnable onFinishedRunnable) {
+ if (mAppearAnimator != null) {
+ mAppearAnimator.cancel();
+ }
+ mAnimationTranslationY = translationDirection * mActualHeight;
+ if (mAppearAnimationFraction == -1.0f) {
+ // not initialized yet, we start anew
+ if (isAppearing) {
+ mAppearAnimationFraction = 0.0f;
+ mAppearAnimationTranslation = mAnimationTranslationY;
+ } else {
+ mAppearAnimationFraction = 1.0f;
+ mAppearAnimationTranslation = 0;
+ }
+ }
+
+ float targetValue;
+ if (isAppearing) {
+ mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
+ mCurrentAlphaInterpolator = mLinearOutSlowInInterpolator;
+ targetValue = 1.0f;
+ } else {
+ mCurrentAppearInterpolator = mFastOutSlowInInterpolator;
+ mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
+ targetValue = 0.0f;
+ }
+ mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
+ targetValue);
+ mAppearAnimator.setInterpolator(mLinearInterpolator);
+ mAppearAnimator.setDuration(
+ (long) (StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR
+ * Math.abs(mAppearAnimationFraction - targetValue)));
+ mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mAppearAnimationFraction = (float) animation.getAnimatedValue();
+ updateAppearAnimationAlpha();
+ updateAppearRect();
+ invalidate();
+ }
+ });
+ if (delay > 0) {
+ // we need to apply the initial state already to avoid drawn frames in the wrong state
+ updateAppearAnimationAlpha();
+ updateAppearRect();
+ mAppearAnimator.setStartDelay(delay);
+ }
+ mAppearAnimator.addListener(new AnimatorListenerAdapter() {
+ private boolean mWasCancelled;
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (onFinishedRunnable != null) {
+ onFinishedRunnable.run();
+ }
+ if (!mWasCancelled) {
+ mAppearAnimationFraction = -1;
+ setOutlineRect(null);
+ enableAppearDrawing(false);
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mWasCancelled = false;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mWasCancelled = true;
+ }
+ });
+ mAppearAnimator.start();
+ }
+
+ private void updateAppearRect() {
+ float inverseFraction = (1.0f - mAppearAnimationFraction);
+ float translationFraction = mCurrentAppearInterpolator.getInterpolation(inverseFraction);
+ float translateYTotalAmount = translationFraction * mAnimationTranslationY;
+ mAppearAnimationTranslation = translateYTotalAmount;
+
+ // handle width animation
+ float widthFraction = (inverseFraction - (1.0f - HORIZONTAL_ANIMATION_START))
+ / (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
+ widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
+ widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
+ float left = (getWidth() * (0.5f - HORIZONTAL_COLLAPSED_REST_PARTIAL / 2.0f) *
+ widthFraction);
+ float right = getWidth() - left;
+
+ // handle top animation
+ float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
+ VERTICAL_ANIMATION_START;
+ heightFraction = Math.max(0.0f, heightFraction);
+ heightFraction = mCurrentAppearInterpolator.getInterpolation(heightFraction);
+
+ float top;
+ float bottom;
+ if (mAnimationTranslationY > 0.0f) {
+ bottom = mActualHeight - heightFraction * mAnimationTranslationY * 0.1f
+ - translateYTotalAmount;
+ top = bottom * heightFraction;
+ } else {
+ top = heightFraction * (mActualHeight + mAnimationTranslationY) * 0.1f -
+ translateYTotalAmount;
+ bottom = mActualHeight * (1 - heightFraction) + top * heightFraction;
+ }
+ mAppearAnimationRect.set(left, top, right, bottom);
+ setOutlineRect(left, top + mAppearAnimationTranslation, right,
+ bottom + mAppearAnimationTranslation);
+ }
+
+ private void updateAppearAnimationAlpha() {
+ int backgroundColor = getBackgroundColor();
+ if (backgroundColor != -1) {
+ float contentAlphaProgress = mAppearAnimationFraction;
+ contentAlphaProgress = contentAlphaProgress / (1.0f - ALPHA_ANIMATION_END);
+ contentAlphaProgress = Math.min(1.0f, contentAlphaProgress);
+ contentAlphaProgress = mCurrentAlphaInterpolator.getInterpolation(contentAlphaProgress);
+ int sourceColor = Color.argb((int) (255 * (1.0f - contentAlphaProgress)),
+ Color.red(backgroundColor), Color.green(backgroundColor),
+ Color.blue(backgroundColor));
+ mAppearAnimationFilter.setColor(sourceColor);
+ mAppearPaint.setColorFilter(mAppearAnimationFilter);
+ }
+ }
+
+ private int getBackgroundColor() {
+ // TODO: get real color
+ return 0xfffafafa;
+ }
+
+ /**
+ * When we draw the appear animation, we render the view in a bitmap and render this bitmap
+ * as a shader of a rect. This call creates the Bitmap and switches the drawing mode,
+ * such that the normal drawing of the views does not happen anymore.
+ *
+ * @param enable Should it be enabled.
+ */
+ private void enableAppearDrawing(boolean enable) {
+ if (enable != mDrawingAppearAnimation) {
+ if (enable) {
+ if (getWidth() == 0 || getActualHeight() == 0) {
+ // TODO: This should not happen, but it can during expansion. Needs
+ // investigation
+ return;
+ }
+ Bitmap bitmap = Bitmap.createBitmap(getWidth(), getActualHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ draw(canvas);
+ mAppearPaint.setShader(new BitmapShader(bitmap, Shader.TileMode.CLAMP,
+ Shader.TileMode.CLAMP));
+ } else {
+ mAppearPaint.setShader(null);
+ }
+ mDrawingAppearAnimation = enable;
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (!mDrawingAppearAnimation) {
+ super.dispatchDraw(canvas);
+ } else {
+ drawAppearRect(canvas);
+ }
+ }
+
+ private void drawAppearRect(Canvas canvas) {
+ canvas.save();
+ canvas.translate(0, mAppearAnimationTranslation);
+ canvas.drawRoundRect(mAppearAnimationRect, mRoundedRectCornerRadius,
+ mRoundedRectCornerRadius, mAppearPaint);
+ canvas.restore();
+ }
+
public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
mOnActivatedListener = onActivatedListener;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 9d758b0..cda84cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -22,6 +22,7 @@
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -46,6 +47,7 @@
import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
@@ -79,6 +81,7 @@
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Locale;
import static com.android.keyguard.KeyguardHostView.OnDismissAction;
@@ -88,6 +91,7 @@
public static final String TAG = "StatusBar";
public static final boolean DEBUG = false;
public static final boolean MULTIUSER_DEBUG = false;
+ private static final boolean USE_NOTIFICATION_LISTENER = false;
protected static final int MSG_SHOW_RECENT_APPS = 1019;
protected static final int MSG_HIDE_RECENT_APPS = 1020;
@@ -160,6 +164,7 @@
protected WindowManager mWindowManager;
protected IWindowManager mWindowManagerService;
+
protected abstract void refreshLayout(int layoutDirection);
protected Display mDisplay;
@@ -175,6 +180,7 @@
*/
protected int mState;
protected boolean mBouncerShowing;
+ protected boolean mShowLockscreenNotifications;
protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
@@ -194,6 +200,9 @@
final int mode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
setZenMode(mode);
+ final boolean show = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1) != 0;
+ setShowLockscreenNotifications(show);
}
};
@@ -269,6 +278,49 @@
}
};
+ private final NotificationListenerService mNotificationListener =
+ new NotificationListenerService() {
+ @Override
+ public void onListenerConnected() {
+ if (DEBUG) Log.d(TAG, "onListenerConnected");
+ final StatusBarNotification[] notifications = getActiveNotifications();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ for (StatusBarNotification sbn : notifications) {
+ addNotificationInternal(sbn);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onNotificationPosted(final StatusBarNotification sbn) {
+ if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mNotificationData.findByKey(sbn.getKey()) != null) {
+ updateNotificationInternal(sbn);
+ } else {
+ addNotificationInternal(sbn);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onNotificationRemoved(final StatusBarNotification sbn) {
+ if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ removeNotificationInternal(sbn.getKey());
+ }
+ });
+ }
+ };
+
private void updateCurrentProfilesCache() {
synchronized (mCurrentProfiles) {
mCurrentProfiles.clear();
@@ -296,6 +348,9 @@
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
mSettingsObserver);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
+ mSettingsObserver);
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
@@ -348,11 +403,22 @@
}
// Set up the initial notification state.
- N = notifications.size();
- for (int i=0; i<N; i++) {
- addNotification(notifications.get(i));
+ if (USE_NOTIFICATION_LISTENER) {
+ try {
+ mNotificationListener.registerAsSystemService(
+ new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
+ UserHandle.USER_ALL);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to register notification listener", e);
+ }
+ } else {
+ N = notifications.size();
+ for (int i=0; i<N; i++) {
+ addNotification(notifications.get(i));
+ }
}
+
if (DEBUG) {
Log.d(TAG, String.format(
"init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
@@ -1065,6 +1131,7 @@
if (rowParent != null) rowParent.removeView(entry.row);
updateRowStates();
updateNotificationIcons();
+ updateSpeedBump();
return entry.notification;
}
@@ -1108,8 +1175,22 @@
if (DEBUG) {
Log.d(TAG, "addNotificationViews: added at " + pos);
}
- updateNotificationIcons();
updateRowStates();
+ updateNotificationIcons();
+ updateSpeedBump();
+ }
+
+ protected void updateSpeedBump() {
+ int n = mNotificationData.size();
+ int speedBumpIndex = -1;
+ for (int i = n-1; i >= 0; i--) {
+ NotificationData.Entry entry = mNotificationData.get(i);
+ if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1
+ && entry.row.isBelowSpeedBump() ) {
+ speedBumpIndex = n - 1 - i;
+ }
+ }
+ mStackScroller.updateSpeedBumpIndex(speedBumpIndex);
}
private void addNotificationViews(StatusBarNotification notification) {
@@ -1129,7 +1210,6 @@
mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
int n = mNotificationData.size();
int visibleNotifications = 0;
- int speedBumpIndex = -1;
boolean onKeyguard = mState == StatusBarState.KEYGUARD;
for (int i = n-1; i >= 0; i--) {
NotificationData.Entry entry = mNotificationData.get(i);
@@ -1150,17 +1230,14 @@
mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
}
} else {
- if (entry.row.getVisibility() == View.GONE) {
+ boolean wasGone = entry.row.getVisibility() == View.GONE;
+ entry.row.setVisibility(View.VISIBLE);
+ if (wasGone) {
// notify the scroller of a child addition
mStackScroller.generateAddAnimation(entry.row);
}
- entry.row.setVisibility(View.VISIBLE);
visibleNotifications++;
}
- if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1
- && entry.row.isBelowSpeedBump() ) {
- speedBumpIndex = n - 1 - i;
- }
}
if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) {
@@ -1168,8 +1245,6 @@
} else {
mKeyguardIconOverflowContainer.setVisibility(View.GONE);
}
-
- mStackScroller.updateSpeedBumpIndex(speedBumpIndex);
}
private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
@@ -1182,6 +1257,10 @@
updateNotificationIcons();
}
+ protected void setShowLockscreenNotifications(boolean show) {
+ mShowLockscreenNotifications = show;
+ }
+
protected abstract void haltTicker();
protected abstract void setAreThereNotifications();
protected abstract void updateNotificationIcons();
@@ -1193,7 +1272,32 @@
return parent != null && parent.indexOfChild(entry.row) == 0;
}
+
+ @Override
+ public void addNotification(StatusBarNotification notification) {
+ if (!USE_NOTIFICATION_LISTENER) {
+ addNotificationInternal(notification);
+ }
+ }
+
+ public abstract void addNotificationInternal(StatusBarNotification notification);
+
+ @Override
+ public void removeNotification(String key) {
+ if (!USE_NOTIFICATION_LISTENER) {
+ removeNotificationInternal(key);
+ }
+ }
+
+ protected abstract void removeNotificationInternal(String key);
+
public void updateNotification(StatusBarNotification notification) {
+ if (!USE_NOTIFICATION_LISTENER) {
+ updateNotificationInternal(notification);
+ }
+ }
+
+ public void updateNotificationInternal(StatusBarNotification notification) {
if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey());
@@ -1269,11 +1373,11 @@
boolean orderUnchanged =
notification.getNotification().when == oldNotification.getNotification().when
&& notification.getScore() == oldNotification.getScore();
- // score now encompasses/supersedes isOngoing()
+ // score now encompasses/supersedes isOngoing()
boolean updateTicker = notification.getNotification().tickerText != null
&& !TextUtils.equals(notification.getNotification().tickerText,
- oldEntry.notification.getNotification().tickerText);
+ oldEntry.notification.getNotification().tickerText);
boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged
&& (orderUnchanged || isTopAnyway)) {
@@ -1305,6 +1409,7 @@
return;
}
updateRowStates();
+ updateSpeedBump();
}
catch (RuntimeException e) {
// It failed to add cleanly. Log, and remove the view from the panel.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index a42c194..843db04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -18,8 +18,8 @@
import android.content.Context;
import android.graphics.Outline;
+import android.graphics.RectF;
import android.util.AttributeSet;
-import android.widget.FrameLayout;
/**
* Like {@link ExpandableView}, but setting an outline for the height and clipping.
@@ -27,9 +27,12 @@
public abstract class ExpandableOutlineView extends ExpandableView {
private final Outline mOutline = new Outline();
+ private boolean mCustomOutline;
+ private float mDensity;
public ExpandableOutlineView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mDensity = getResources().getDisplayMetrics().density;
}
@Override
@@ -50,11 +53,37 @@
updateOutline();
}
- private void updateOutline() {
- mOutline.setRect(0,
- mClipTopAmount,
- getWidth(),
- Math.max(mActualHeight, mClipTopAmount));
+ protected void setOutlineRect(RectF rect) {
+ if (rect != null) {
+ setOutlineRect(rect.left, rect.top, rect.right, rect.bottom);
+ } else {
+ mCustomOutline = false;
+ updateOutline();
+ }
+ }
+
+ protected void setOutlineRect(float left, float top, float right, float bottom) {
+ mCustomOutline = true;
+
+ int rectLeft = (int) left;
+ int rectTop = (int) top;
+ int rectRight = (int) right;
+ int rectBottom = (int) bottom;
+
+ // Outlines need to be at least 1 dp
+ rectBottom = (int) Math.max(top + mDensity, rectBottom);
+ rectRight = (int) Math.max(left + mDensity, rectRight);
+ mOutline.setRect(rectLeft, rectTop, rectRight, rectBottom);
setOutline(mOutline);
}
+
+ private void updateOutline() {
+ if (!mCustomOutline) {
+ mOutline.setRect(0,
+ mClipTopAmount,
+ getWidth(),
+ Math.max(mActualHeight, mClipTopAmount));
+ setOutline(mOutline);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index eaaac10..088f076 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -205,6 +205,21 @@
}
/**
+ * Perform a remove animation on this view.
+ *
+ * @param translationDirection The direction value from [-1 ... 1] indicating in which the
+ * animation should be performed. A value of -1 means that The
+ * remove animation should be performed upwards,
+ * such that the child appears to be going away to the top. 1
+ * Should mean the opposite.
+ * @param onFinishedRunnable A runnable which should be run when the animation is finished.
+ */
+ public abstract void performRemoveAnimation(float translationDirection,
+ Runnable onFinishedRunnable);
+
+ public abstract void performAddAnimation(long delay);
+
+ /**
* A listener notifying when {@link #getActualHeight} changes.
*/
public interface OnHeightChangedListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
index bd511f2..0555879 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
@@ -50,7 +50,7 @@
for (int i = 0; i < n; i++) {
final StatusBarNotification sbn = mIntercepted.valueAt(i);
sbn.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false);
- mBar.addNotification(sbn);
+ mBar.addNotificationInternal(sbn);
}
mIntercepted.clear();
updateSyntheticNotification();
@@ -88,7 +88,7 @@
private void updateSyntheticNotification() {
if (mIntercepted.isEmpty()) {
if (mSynKey != null) {
- mBar.removeNotification(mSynKey);
+ mBar.removeNotificationInternal(mSynKey);
mSynKey = null;
}
return;
@@ -107,9 +107,9 @@
mBar.getCurrentUserHandle());
if (mSynKey == null) {
mSynKey = sbn.getKey();
- mBar.addNotification(sbn);
+ mBar.addNotificationInternal(sbn);
} else {
- mBar.updateNotification(sbn);
+ mBar.updateNotificationInternal(sbn);
}
final NotificationData.Entry entry = mBar.mNotificationData.findByKey(mSynKey);
entry.row.setOnClickListener(mSynClickListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index 3c080fe..1c2ca91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -34,7 +34,6 @@
public NotificationBackgroundView(Context context, AttributeSet attrs) {
super(context, attrs);
- setWillNotDraw(false);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
index 8ae503a..a84daef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
@@ -103,7 +103,11 @@
@Override
public int getIntrinsicHeight() {
- return getActualHeight();
+ if (mCurrentAnimator != null) {
+ // expand animation is running
+ return getActualHeight();
+ }
+ return mIsExpanded ? getHeight() : mCollapsedHeight;
}
@Override
@@ -184,7 +188,7 @@
}
public void performVisibilityAnimation(boolean nowVisible) {
- animateDivider(nowVisible);
+ animateDivider(nowVisible, null /* onFinishedRunnable */);
// Animate explanation Text
if (mIsExpanded) {
@@ -192,7 +196,14 @@
}
}
- public void animateDivider(boolean nowVisible) {
+ /**
+ * Animate the divider to a new visibility.
+ *
+ * @param nowVisible should it now be visible
+ * @param onFinishedRunnable A runnable which should be run when the animation is
+ * finished.
+ */
+ public void animateDivider(boolean nowVisible, Runnable onFinishedRunnable) {
if (nowVisible != mDividerVisible) {
// Animate dividers
float endValue = nowVisible ? 1.0f : 0.0f;
@@ -204,7 +215,8 @@
.scaleX(endValue)
.scaleY(endValue)
.translationX(endTranslationXLeft)
- .setInterpolator(mFastOutSlowInInterpolator);
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .withEndAction(onFinishedRunnable);
mLineRight.animate()
.alpha(endValue)
.withLayer()
@@ -216,6 +228,10 @@
// Animate dots
mDots.performVisibilityAnimation(nowVisible);
mDividerVisible = nowVisible;
+ } else {
+ if (onFinishedRunnable != null) {
+ onFinishedRunnable.run();
+ }
}
}
@@ -250,6 +266,16 @@
}
}
+ @Override
+ public void performRemoveAnimation(float translationDirection, Runnable onFinishedRunnable) {
+ performVisibilityAnimation(false);
+ }
+
+ @Override
+ public void performAddAnimation(long delay) {
+ performVisibilityAnimation(true);
+ }
+
private void resetExplanationText() {
mExplanationText.setTranslationY(0);
mExplanationText.setVisibility(INVISIBLE);
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 5e9ce21..714ad06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -29,18 +29,11 @@
import android.provider.MediaStore;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
/**
@@ -48,7 +41,8 @@
* text.
*/
public class KeyguardBottomAreaView extends FrameLayout
- implements SwipeAffordanceView.AffordanceListener {
+ implements SwipeAffordanceView.AffordanceListener,
+ UnlockMethodCache.OnUnlockMethodChangedListener {
final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
@@ -60,9 +54,7 @@
private PowerManager mPowerManager;
private ActivityStarter mActivityStarter;
-
- private LockPatternUtils mLockPatternUtils;
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private UnlockMethodCache mUnlockMethodCache;
public KeyguardBottomAreaView(Context context) {
super(context);
@@ -89,13 +81,12 @@
mLockIcon = (ImageView) findViewById(R.id.lock_icon);
mCameraButton.setAffordanceListener(this);
mPhoneButton.setAffordanceListener(this);
- mLockPatternUtils = new LockPatternUtils(getContext());
- mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
- KeyguardUpdateMonitor.getInstance(getContext()).registerCallback(mCallback);
watchForDevicePolicyChanges();
watchForAccessibilityChanges();
updateCameraVisibility();
updatePhoneVisibility();
+ mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
+ mUnlockMethodCache.addListener(this);
updateTrust();
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
}
@@ -211,28 +202,14 @@
if (getVisibility() != VISIBLE) {
return;
}
- int user = mLockPatternUtils.getCurrentUser();
- boolean trust = !mLockPatternUtils.isSecure() ||
- mKeyguardUpdateMonitor.getUserHasTrust(user);
-
- int iconRes = trust ? R.drawable.ic_lock_open_24dp : R.drawable.ic_lock_24dp;
+ int iconRes = mUnlockMethodCache.isMethodInsecure()
+ ? R.drawable.ic_lock_open_24dp
+ : R.drawable.ic_lock_24dp;
mLockIcon.setImageResource(iconRes);
}
- final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onScreenTurnedOn() {
- updateTrust();
- }
-
- @Override
- public void onUserSwitchComplete(int userId) {
- updateTrust();
- }
-
- @Override
- public void onTrustChanged(int userId) {
- updateTrust();
- }
- };
+ @Override
+ public void onMethodSecureChanged(boolean methodSecure) {
+ updateTrust();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 9138867..2bb80bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -54,10 +54,6 @@
mWindowManager = windowManager;
}
- public void prepare() {
- ensureView();
- }
-
public void show() {
ensureView();
@@ -65,7 +61,6 @@
// Keyguard. If we need to authenticate, show the bouncer.
if (!mKeyguardView.dismiss()) {
mRoot.setVisibility(View.VISIBLE);
- mKeyguardView.requestFocus();
mKeyguardView.onResume();
}
}
@@ -76,11 +71,15 @@
show();
}
- public void hide() {
+ public void hide(boolean destroyView) {
if (mKeyguardView != null) {
mKeyguardView.cleanUp();
}
- removeView();
+ if (destroyView) {
+ removeView();
+ } else if (mRoot != null) {
+ mRoot.setVisibility(View.INVISIBLE);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
new file mode 100644
index 0000000..6a83a5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -0,0 +1,186 @@
+/*
+ * 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.content.res.Resources;
+import android.graphics.Path;
+import android.view.animation.PathInterpolator;
+
+import com.android.systemui.R;
+
+/**
+ * Utility class to calculate the clock position and top padding of notifications on Keyguard.
+ */
+public class KeyguardClockPositionAlgorithm {
+
+ private static final float SLOW_DOWN_FACTOR = 0.4f;
+
+ private static final float CLOCK_RUBBERBAND_FACTOR_MIN = 0.08f;
+ private static final float CLOCK_RUBBERBAND_FACTOR_MAX = 0.8f;
+
+ private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN = 1.4f;
+ private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX = 3.2f;
+
+ private int mClockNotificationsMarginMin;
+ private int mClockNotificationsMarginMax;
+ private float mClockYFractionMin;
+ private float mClockYFractionMax;
+ private int mMaxKeyguardNotifications;
+ private int mMaxPanelHeight;
+ private float mExpandedHeight;
+ private int mNotificationCount;
+ private int mHeight;
+ private int mKeyguardStatusHeight;
+
+ /**
+ * The number (fractional) of notifications the "more" card counts when calculating how many
+ * notifications are currently visible for the y positioning of the clock.
+ */
+ private float mMoreCardNotificationAmount;
+
+ private static final PathInterpolator sSlowDownInterpolator;
+
+ static {
+ Path path = new Path();
+ path.moveTo(0, 0);
+ path.cubicTo(0.3f, 0.875f, 0.6f, 1f, 1f, 1f);
+ sSlowDownInterpolator = new PathInterpolator(path);
+ }
+
+ /**
+ * Refreshes the dimension values.
+ */
+ public void loadDimens(Resources res) {
+ mClockNotificationsMarginMin = res.getDimensionPixelSize(
+ R.dimen.keyguard_clock_notifications_margin_min);
+ mClockNotificationsMarginMax = res.getDimensionPixelSize(
+ R.dimen.keyguard_clock_notifications_margin_max);
+ mClockYFractionMin = res.getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
+ mClockYFractionMax = res.getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
+ mMoreCardNotificationAmount =
+ (float) res.getDimensionPixelSize(R.dimen.notification_summary_height) /
+ res.getDimensionPixelSize(R.dimen.notification_min_height);
+ }
+
+ public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight,
+ int notificationCount, int height, int keyguardStatusHeight) {
+ mMaxKeyguardNotifications = maxKeyguardNotifications;
+ mMaxPanelHeight = maxPanelHeight;
+ mExpandedHeight = expandedHeight;
+ mNotificationCount = notificationCount;
+ mHeight = height;
+ mKeyguardStatusHeight = keyguardStatusHeight;
+ }
+
+ public void run(Result result) {
+ int y = getClockY() - mKeyguardStatusHeight/2;
+ float clockAdjustment = getClockYExpansionAdjustment();
+ float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
+ result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
+ int clockNotificationsPadding = getClockNotificationsPadding()
+ + result.stackScrollerPaddingAdjustment;
+ int padding = y + clockNotificationsPadding;
+ y += clockAdjustment;
+ result.clockY = y;
+ result.stackScrollerPadding = mKeyguardStatusHeight + padding;
+ result.clockAlpha = getClockAlpha(result.stackScrollerPadding
+ - (y + mKeyguardStatusHeight));
+ }
+
+ private int getClockNotificationsPadding() {
+ float t = getNotificationAmountT();
+ t = Math.min(t, 1.0f);
+ return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax);
+ }
+
+ private float getClockYFraction() {
+ float t = getNotificationAmountT();
+ t = Math.min(t, 1.0f);
+ return (1 - t) * mClockYFractionMax + t * mClockYFractionMin;
+ }
+
+ private int getClockY() {
+ return (int) (getClockYFraction() * mHeight);
+ }
+
+ private float getClockYExpansionAdjustment() {
+ float rubberbandFactor = getClockYExpansionRubberbandFactor();
+ float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight));
+ float t = value / mMaxPanelHeight;
+ float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR
+ * mMaxPanelHeight;
+ if (mNotificationCount == 0) {
+ return (-2*value + slowedDownValue)/3;
+ } else {
+ return slowedDownValue;
+ }
+ }
+
+ private float getClockYExpansionRubberbandFactor() {
+ float t = getNotificationAmountT();
+ t = Math.min(t, 1.0f);
+ t = (float) Math.pow(t, 0.3f);
+ return (1 - t) * CLOCK_RUBBERBAND_FACTOR_MAX + t * CLOCK_RUBBERBAND_FACTOR_MIN;
+ }
+
+ private float getTopPaddingAdjMultiplier() {
+ float t = getNotificationAmountT();
+ t = Math.min(t, 1.0f);
+ return (1 - t) * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN
+ + t * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX;
+ }
+
+ private float getClockAlpha(int clockNotificationPadding) {
+ float t = getNotificationAmountT();
+ t = (float) Math.pow(t, 0.3f);
+ float multiplier = 1 + 2 * (1 - t);
+ float alpha = 1 + (float) clockNotificationPadding * multiplier / mKeyguardStatusHeight * 3;
+ return Math.max(0, Math.min(1, alpha));
+ }
+
+ /**
+ * @return a value from 0 to 1 depending on how many notification there are
+ */
+ private float getNotificationAmountT() {
+ return mNotificationCount
+ / (mMaxKeyguardNotifications + mMoreCardNotificationAmount);
+ }
+
+ public static class Result {
+
+ /**
+ * The y translation of the clock.
+ */
+ public int clockY;
+
+ /**
+ * The alpha value of the clock.
+ */
+ public float clockAlpha;
+
+ /**
+ * The top padding of the stack scroller, in pixels.
+ */
+ public int stackScrollerPadding;
+
+ /**
+ * The top padding adjustment of the stack scroller, in pixels. This value is used to adjust
+ * the padding, but not the overall panel size.
+ */
+ public int stackScrollerPaddingAdjustment;
+ }
+}
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 123a4f0..f5252a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -19,7 +19,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
@@ -48,6 +47,7 @@
PhoneStatusBar mStatusBar;
private StatusBarHeaderView mHeader;
private View mQsContainer;
+ private View mQsPanel;
private View mKeyguardStatusView;
private ObservableScrollView mScrollView;
private View mStackScrollerContainer;
@@ -66,6 +66,7 @@
*/
private boolean mIntercepting;
private boolean mQsExpanded;
+ private boolean mKeyguardShowing;
private float mInitialHeightOnTouch;
private float mInitialTouchX;
private float mInitialTouchY;
@@ -75,6 +76,7 @@
private int mQsMinExpansionHeight;
private int mQsMaxExpansionHeight;
private int mMinStackHeight;
+ private int mQsPeekHeight;
private float mNotificationTranslation;
private int mStackScrollerIntrinsicPadding;
private boolean mQsExpansionEnabled = true;
@@ -82,19 +84,14 @@
private FlingAnimationUtils mFlingAnimationUtils;
private int mStatusBarMinHeight;
- private int mClockNotificationsMarginMin;
- private int mClockNotificationsMarginMax;
- private float mClockYFractionMin;
- private float mClockYFractionMax;
private Interpolator mFastOutSlowInInterpolator;
private ObjectAnimator mClockAnimator;
private int mClockAnimationTarget = -1;
-
- /**
- * The number (fractional) of notifications the "more" card counts when calculating how many
- * notifications are currently visible for the y positioning of the clock.
- */
- private float mMoreCardNotificationAmount;
+ private int mTopPaddingAdjustment;
+ private KeyguardClockPositionAlgorithm mClockPositionAlgorithm =
+ new KeyguardClockPositionAlgorithm();
+ private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
+ new KeyguardClockPositionAlgorithm.Result();
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -124,6 +121,7 @@
mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
mStackScrollerContainer = findViewById(R.id.notification_container_parent);
mQsContainer = findViewById(R.id.quick_settings_container);
+ mQsPanel = findViewById(R.id.quick_settings_panel);
mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
mScrollView.setListener(this);
mNotificationStackScroller = (NotificationStackScrollLayout)
@@ -139,34 +137,24 @@
mNotificationTopPadding = getResources().getDimensionPixelSize(
R.dimen.notifications_top_padding);
mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
- mClockNotificationsMarginMin = getResources().getDimensionPixelSize(
- R.dimen.keyguard_clock_notifications_margin_min);
- mClockNotificationsMarginMax = getResources().getDimensionPixelSize(
- R.dimen.keyguard_clock_notifications_margin_max);
- mClockYFractionMin =
- getResources().getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
- mClockYFractionMax =
- getResources().getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
- mMoreCardNotificationAmount =
- (float) getResources().getDimensionPixelSize(R.dimen.notification_summary_height) /
- getResources().getDimensionPixelSize(R.dimen.notification_min_height);
mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.4f);
mStatusBarMinHeight = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
+ mQsPeekHeight = getResources().getDimensionPixelSize(R.dimen.qs_peek_height);
+ mClockPositionAlgorithm.loadDimens(getResources());
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- if (!mQsExpanded) {
- positionClockAndNotifications();
- }
// Calculate quick setting heights.
- mQsMinExpansionHeight = mHeader.getCollapsedHeight();
+ mQsMinExpansionHeight = mHeader.getCollapsedHeight() + mQsPeekHeight;
mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight();
- if (mQsExpansionHeight == 0) {
- mQsExpansionHeight = mQsMinExpansionHeight;
+ if (!mQsExpanded) {
+ setQsExpansion(mQsMinExpansionHeight);
+ positionClockAndNotifications();
+ mNotificationStackScroller.setStackHeight(getExpandedHeight());
}
}
@@ -177,17 +165,26 @@
private void positionClockAndNotifications() {
boolean animateClock = mNotificationStackScroller.isAddOrRemoveAnimationPending();
if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
- mStackScrollerIntrinsicPadding = mHeader.getBottom() + mNotificationTopPadding;
+ mStackScrollerIntrinsicPadding = mHeader.getBottom() + mQsPeekHeight
+ + mNotificationTopPadding;
+ mTopPaddingAdjustment = 0;
} else {
- int notificationCount = mNotificationStackScroller.getNotGoneChildCount();
- int y = getClockY(notificationCount) - mKeyguardStatusView.getHeight()/2;
- int padding = getClockNotificationsPadding(notificationCount);
+ mClockPositionAlgorithm.setup(
+ mStatusBar.getMaxKeyguardNotifications(),
+ getMaxPanelHeight(),
+ getExpandedHeight(),
+ mNotificationStackScroller.getNotGoneChildCount(),
+ getHeight(),
+ mKeyguardStatusView.getHeight());
+ mClockPositionAlgorithm.run(mClockPositionResult);
if (animateClock || mClockAnimator != null) {
- startClockAnimation(y);
+ startClockAnimation(mClockPositionResult.clockY);
} else {
- mKeyguardStatusView.setY(y);
+ mKeyguardStatusView.setY(mClockPositionResult.clockY);
}
- mStackScrollerIntrinsicPadding = y + mKeyguardStatusView.getHeight() + padding;
+ applyClockAlpha(mClockPositionResult.clockAlpha);
+ mStackScrollerIntrinsicPadding = mClockPositionResult.stackScrollerPadding;
+ mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
}
mNotificationStackScroller.setTopPadding(mStackScrollerIntrinsicPadding,
mAnimateNextTopPaddingChange || animateClock);
@@ -218,28 +215,19 @@
mClockAnimationTarget = -1;
}
});
- StackStateAnimator.startInstantly(mClockAnimator);
+ mClockAnimator.start();
return true;
}
});
}
- private int getClockNotificationsPadding(int notificationCount) {
- float t = notificationCount
- / (mStatusBar.getMaxKeyguardNotifications() + mMoreCardNotificationAmount);
- t = Math.min(t, 1.0f);
- return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax);
- }
-
- private float getClockYFraction(int notificationCount) {
- float t = notificationCount
- / (mStatusBar.getMaxKeyguardNotifications() + mMoreCardNotificationAmount);
- t = Math.min(t, 1.0f);
- return (1 - t) * mClockYFractionMax + t * mClockYFractionMin;
- }
-
- private int getClockY(int notificationCount) {
- return (int) (getClockYFraction(notificationCount) * getHeight());
+ private void applyClockAlpha(float alpha) {
+ if (alpha != 1.0f) {
+ mKeyguardStatusView.setLayerType(LAYER_TYPE_HARDWARE, null);
+ } else {
+ mKeyguardStatusView.setLayerType(LAYER_TYPE_NONE, null);
+ }
+ mKeyguardStatusView.setAlpha(alpha);
}
public void animateToFullShade() {
@@ -366,10 +354,12 @@
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- // Block request so we can still intercept the scrolling when QS is expanded.
- if (!mQsExpanded) {
- super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ // Block request when interacting with the scroll view so we can still intercept the
+ // scrolling when QS is expanded.
+ if (mScrollView.isDispatchingTouchEvent()) {
+ return;
}
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
}
private void flingWithCurrentVelocity() {
@@ -466,31 +456,41 @@
setQsExpansion(height);
}
- private void expandQs() {
- mHeader.setExpanded(true);
- mNotificationStackScroller.setEnabled(false);
- mScrollView.setVisibility(View.VISIBLE);
- mQsExpanded = true;
+ private void setQsExpanded(boolean expanded) {
+ boolean changed = mQsExpanded != expanded;
+ if (changed) {
+ mQsExpanded = expanded;
+ updateQsState();
+ }
}
- private void collapseQs() {
- mHeader.setExpanded(false);
- mNotificationStackScroller.setEnabled(true);
- mScrollView.setVisibility(View.INVISIBLE);
- mQsExpanded = false;
+ public void setKeyguardShowing(boolean keyguardShowing) {
+ mKeyguardShowing = keyguardShowing;
+ updateQsState();
+ }
+
+ private void updateQsState() {
+ mHeader.setExpanded(mQsExpanded);
+ mNotificationStackScroller.setEnabled(!mQsExpanded);
+ mQsPanel.setVisibility(mQsExpanded ? View.VISIBLE : View.INVISIBLE);
+ mQsContainer.setVisibility(mKeyguardShowing && !mQsExpanded
+ ? View.INVISIBLE
+ : View.VISIBLE);
+ mScrollView.setTouchEnabled(mQsExpanded);
}
private void setQsExpansion(float height) {
height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
if (height > mQsMinExpansionHeight && !mQsExpanded) {
- expandQs();
+ setQsExpanded(true);
} else if (height <= mQsMinExpansionHeight && mQsExpanded) {
- collapseQs();
+ setQsExpanded(false);
}
mQsExpansionHeight = height;
- mHeader.setExpansion(height);
+ mHeader.setExpansion(height - mQsPeekHeight);
setQsTranslation(height);
setQsStackScrollerPadding(height);
+ mStatusBar.userActivity();
}
private void setQsTranslation(float height) {
@@ -626,7 +626,7 @@
int emptyBottomMargin = mStackScrollerContainer.getHeight()
- mNotificationStackScroller.getHeight()
+ mNotificationStackScroller.getEmptyBottomMargin();
- int maxHeight = maxPanelHeight - emptyBottomMargin;
+ int maxHeight = maxPanelHeight - emptyBottomMargin - mTopPaddingAdjustment;
maxHeight = Math.max(maxHeight, mStatusBarMinHeight);
return maxHeight;
}
@@ -637,6 +637,9 @@
@Override
protected void onHeightUpdated(float expandedHeight) {
+ if (!mQsExpanded) {
+ positionClockAndNotifications();
+ }
mNotificationStackScroller.setStackHeight(expandedHeight);
}
@@ -653,6 +656,23 @@
}
@Override
+ protected void onOverExpansionChanged(float overExpansion) {
+ float currentOverScroll = mNotificationStackScroller.getCurrentOverScrolledPixels(true);
+ mNotificationStackScroller.setOverScrolledPixels(currentOverScroll + overExpansion
+ - mOverExpansion, true /* onTop */, false /* animate */);
+ super.onOverExpansionChanged(overExpansion);
+ }
+
+ @Override
+ protected void onTrackingStopped() {
+ super.onTrackingStopped();
+ mOverExpansion = 0.0f;
+ mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */,
+ true /* animate */);
+ }
+
+
+ @Override
public void onHeightChanged(ExpandableView view) {
requestPanelHeightUpdate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
index ba0b66e..ea5b309 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;
@@ -28,6 +29,8 @@
private Listener mListener;
private int mLastOverscrollAmount;
+ private boolean mDispatchingTouchEvent;
+ private boolean mTouchEnabled = true;
public ObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -37,10 +40,18 @@
mListener = listener;
}
+ public void setTouchEnabled(boolean touchEnabled) {
+ mTouchEnabled = touchEnabled;
+ }
+
public boolean isScrolledToBottom() {
return getScrollY() == getMaxScrollY();
}
+ public boolean isDispatchingTouchEvent() {
+ return mDispatchingTouchEvent;
+ }
+
private int getMaxScrollY() {
int scrollRange = 0;
if (getChildCount() > 0) {
@@ -52,6 +63,17 @@
}
@Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (!mTouchEnabled) {
+ return false;
+ }
+ mDispatchingTouchEvent = true;
+ boolean result = super.dispatchTouchEvent(ev);
+ mDispatchingTouchEvent = false;
+ return result;
+ }
+
+ @Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index b6a43a7..7c1f2cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -38,6 +38,7 @@
public class PanelView extends FrameLayout {
public static final boolean DEBUG = PanelBar.DEBUG;
public static final String TAG = PanelView.class.getSimpleName();
+ protected float mOverExpansion;
private final void logf(String fmt, Object... args) {
Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
@@ -402,7 +403,12 @@
public void setExpandedHeightInternal(float h) {
float fh = getMaxPanelHeight();
- mExpandedHeight = h;
+ mExpandedHeight = Math.min(fh, h);
+ float overExpansion = h - fh;
+ overExpansion = Math.max(0, overExpansion);
+ if (overExpansion != mOverExpansion) {
+ onOverExpansionChanged(overExpansion);
+ }
if (DEBUG) {
logf("setExpansion: height=%.1f fh=%.1f tracking=%s", h, fh, mTracking ? "T" : "f");
@@ -412,6 +418,10 @@
mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh);
}
+ protected void onOverExpansionChanged(float overExpansion) {
+ mOverExpansion = overExpansion;
+ }
+
protected void onHeightUpdated(float expandedHeight) {
requestLayout();
}
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 3b7a3a8..aa04c47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -121,10 +121,12 @@
import com.android.systemui.statusbar.policy.LocationControllerImpl;
import com.android.systemui.statusbar.policy.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
+import com.android.systemui.volume.VolumeComponent;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -196,8 +198,9 @@
NetworkControllerImpl mNetworkController;
RotationLockControllerImpl mRotationLockController;
UserInfoController mUserInfoController;
- ZenModeControllerImpl mZenModeController;
+ ZenModeController mZenModeController;
CastControllerImpl mCastController;
+ VolumeComponent mVolumeComponent;
int mNaturalBarHeight = -1;
int mIconSize = -1;
@@ -372,6 +375,7 @@
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private ViewMediatorCallback mKeyguardViewMediatorCallback;
+ private ScrimController mScrimController;
private final Runnable mAutohide = new Runnable() {
@Override
@@ -500,6 +504,12 @@
}
@Override
+ protected void setShowLockscreenNotifications(boolean show) {
+ super.setShowLockscreenNotifications(show);
+ updateStackScrollerState();
+ }
+
+ @Override
public void start() {
mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
@@ -638,9 +648,12 @@
SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_speed_bump, mStackScroller, false);
mStackScroller.setSpeedBumpView(speedBump);
-
mExpandedContents = mStackScroller;
+ mScrimController = new ScrimController(mStatusBarWindow.findViewById(R.id.scrim_behind),
+ mStatusBarWindow.findViewById(R.id.scrim_in_front));
+ mStatusBarView.setScrimController(mScrimController);
+
mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
mHeader.setActivityStarter(this);
mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
@@ -680,7 +693,8 @@
mRotationLockController = new RotationLockControllerImpl(mContext);
}
mUserInfoController = new UserInfoController(mContext);
- mZenModeController = new ZenModeControllerImpl(mContext, mHandler);
+ mVolumeComponent = getComponent(VolumeComponent.class);
+ mZenModeController = mVolumeComponent.getZenController();
mCastController = new CastControllerImpl(mContext);
final SignalClusterView signalCluster =
(SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
@@ -743,7 +757,7 @@
final QSTileHost qsh = new QSTileHost(mContext, this,
mBluetoothController, mLocationController, mRotationLockController,
mNetworkController, mZenModeController, null /*tethering*/,
- mCastController);
+ mCastController, mVolumeComponent);
for (QSTile<?> tile : qsh.getTiles()) {
mQSPanel.addTile(tile);
}
@@ -775,7 +789,7 @@
private void startKeyguard() {
KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
- mStatusBarWindow, mStatusBarWindowManager);
+ mStatusBarWindow, mStatusBarWindowManager, mScrimController);
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
}
@@ -966,7 +980,7 @@
private void addHeadsUpView() {
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
@@ -1029,7 +1043,7 @@
}
@Override
- public void addNotification(StatusBarNotification notification) {
+ public void addNotificationInternal(StatusBarNotification notification) {
if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore());
Entry shadeEntry = createNotificationViews(notification);
if (shadeEntry == null) {
@@ -1097,7 +1111,7 @@
}
@Override
- public void removeNotification(String key) {
+ public void removeNotificationInternal(String key) {
StatusBarNotification old = removeNotificationViews(key);
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
@@ -1446,6 +1460,10 @@
startActivityDismissingKeyguard(intent, false);
}
+ public ScrimController getScrimController() {
+ return mScrimController;
+ }
+
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
@@ -2770,27 +2788,34 @@
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
mKeyguardBottomArea.setVisibility(View.VISIBLE);
mHeader.setKeyguardShowing(true);
+ mNotificationPanel.setKeyguardShowing(true);
+ mScrimController.setKeyguardShowing(true);
} else {
mKeyguardBottomArea.setVisibility(View.GONE);
mHeader.setKeyguardShowing(false);
+ mNotificationPanel.setKeyguardShowing(false);
+ mScrimController.setKeyguardShowing(false);
}
updateStackScrollerState();
updatePublicMode();
updateRowStates();
+ updateSpeedBump();
checkBarModes();
updateNotificationIcons();
updateCarrierLabelVisibility(false);
}
public void updateStackScrollerState() {
+ if (mStackScroller == null) return;
mStackScroller.setDimmed(mState == StatusBarState.KEYGUARD, false /* animate */);
+ mStackScroller.setVisibility(!mShowLockscreenNotifications && mState == StatusBarState.KEYGUARD
+ ? View.INVISIBLE : View.VISIBLE);
}
public void userActivity() {
- if (mState == StatusBarState.KEYGUARD) {
- mKeyguardViewMediatorCallback.userActivity();
- }
+ mHandler.removeCallbacks(mUserActivity);
+ mHandler.post(mUserActivity);
}
public boolean interceptMediaKey(KeyEvent event) {
@@ -2948,4 +2973,13 @@
public void onScreenTurnedOn() {
mStackScroller.setAnimationsEnabled(true);
}
+
+ private final Runnable mUserActivity = new Runnable() {
+ @Override
+ public void run() {
+ if (mState == StatusBarState.KEYGUARD) {
+ mKeyguardViewMediatorCallback.userActivity();
+ }
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 084bfcf..5fdf5bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -36,21 +36,16 @@
private static final boolean DEBUG_GESTURES = true;
PhoneStatusBar mBar;
- int mScrimColor;
- int mScrimColorKeyguard;
- PanelView mFadingPanel = null;
PanelView mLastFullyOpenedPanel = null;
PanelView mNotificationPanel;
- private boolean mShouldFade;
private final PhoneStatusBarTransitions mBarTransitions;
+ private ScrimController mScrimController;
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
Resources res = getContext().getResources();
- mScrimColor = res.getColor(R.color.notification_panel_scrim_color);
- mScrimColorKeyguard = res.getColor(R.color.notification_panel_scrim_color_keyguard);
mBarTransitions = new PhoneStatusBarTransitions(this);
}
@@ -62,6 +57,10 @@
mBar = bar;
}
+ public void setScrimController(ScrimController scrimController) {
+ mScrimController = scrimController;
+ }
+
@Override
public void onFinishInflate() {
mBarTransitions.init();
@@ -110,27 +109,11 @@
}
@Override
- public void startOpeningPanel(PanelView panel) {
- super.startOpeningPanel(panel);
- // we only want to start fading if this is the "first" or "last" panel,
- // which is kind of tricky to determine
- mShouldFade = (mFadingPanel == null || mFadingPanel.isFullyExpanded());
- if (DEBUG) {
- Log.v(TAG, "start opening: " + panel + " shouldfade=" + mShouldFade);
- }
- mFadingPanel = panel;
- }
-
- @Override
public void onAllPanelsCollapsed() {
super.onAllPanelsCollapsed();
// give animations time to settle
mBar.makeExpandedInvisibleSoon();
- mFadingPanel = null;
mLastFullyOpenedPanel = null;
- if (mScrimColor != 0 && ActivityManager.isHighEndGfx() && mBar.mStatusBarWindow != null) {
- mBar.mStatusBarWindow.setBackgroundColor(0);
- }
}
@Override
@@ -139,9 +122,7 @@
if (openPanel != mLastFullyOpenedPanel) {
openPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
- mFadingPanel = openPanel;
mLastFullyOpenedPanel = openPanel;
- mShouldFade = true; // now you own the fade, mister
}
@Override
@@ -163,6 +144,7 @@
public void onTrackingStarted(PanelView panel) {
super.onTrackingStarted(panel);
mBar.onTrackingStarted();
+ mScrimController.onTrackingStarted();
}
@Override
@@ -184,27 +166,7 @@
Log.v(TAG, "panelExpansionChanged: f=" + frac);
}
- if (panel == mFadingPanel && mScrimColor != 0 && ActivityManager.isHighEndGfx()
- && mBar.mStatusBarWindow != null) {
- if (mShouldFade) {
- int scrimColor = (mBar.getBarState() == StatusBarState.KEYGUARD
- || mBar.getBarState() == StatusBarState.SHADE_LOCKED)
- ? mScrimColorKeyguard
- : mScrimColor;
- frac = mPanelExpandedFractionSum; // don't judge me
- // let's start this 20% of the way down the screen
- frac = frac * 1.2f - 0.2f;
- if (frac <= 0) {
- mBar.mStatusBarWindow.setBackgroundColor(0);
- } else {
- // woo, special effects
- final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
- // attenuate background color alpha by k
- final int color = (int) ((scrimColor >>> 24) * k) << 24 | (scrimColor & 0xFFFFFF);
- mBar.mStatusBarWindow.setBackgroundColor(color);
- }
- }
- }
+ mScrimController.setPanelExpansion(frac);
// fade out the panel as it gets buried into the status bar to avoid overdrawing the
// status bar on the last frame of a close animation
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 7029898..1344703 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -21,6 +21,7 @@
import android.os.HandlerThread;
import android.os.Looper;
+import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.tiles.AirplaneModeTile;
import com.android.systemui.qs.tiles.BluetoothTile;
@@ -29,11 +30,10 @@
import com.android.systemui.qs.tiles.CellularTile;
import com.android.systemui.qs.tiles.ColorInversionTile;
import com.android.systemui.qs.tiles.LocationTile;
-import com.android.systemui.qs.tiles.RingerModeTile;
+import com.android.systemui.qs.tiles.NotificationsTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.qs.tiles.HotspotTile;
import com.android.systemui.qs.tiles.WifiTile;
-import com.android.systemui.qs.tiles.ZenModeTile;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.TetheringController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.volume.VolumeComponent;
import java.util.ArrayList;
import java.util.List;
@@ -60,13 +61,15 @@
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 int mFeedbackStartDelay;
public QSTileHost(Context context, PhoneStatusBar statusBar,
BluetoothController bluetooth, LocationController location,
RotationLockController rotation, NetworkController network,
ZenModeController zen, TetheringController tethering,
- CastController cast) {
+ CastController cast, VolumeComponent volume) {
mContext = context;
mStatusBar = statusBar;
mBluetooth = bluetooth;
@@ -76,6 +79,7 @@
mZen = zen;
mTethering = tethering;
mCast = cast;
+ mVolume = volume;
final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName());
ht.start();
@@ -86,8 +90,7 @@
mTiles.add(new ColorInversionTile(this));
mTiles.add(new CellularTile(this));
mTiles.add(new AirplaneModeTile(this));
- mTiles.add(new ZenModeTile(this));
- mTiles.add(new RingerModeTile(this));
+ mTiles.add(new NotificationsTile(this));
mTiles.add(new RotationLockTile(this));
mTiles.add(new LocationTile(this));
mTiles.add(new CastTile(this));
@@ -103,6 +106,7 @@
}
};
mUserTracker.startTracking();
+ mFeedbackStartDelay = mContext.getResources().getInteger(R.integer.feedback_start_delay);
}
@Override
@@ -112,7 +116,7 @@
@Override
public void startSettingsActivity(final Intent intent) {
- mStatusBar.postStartSettingsActivity(intent, QSTile.FEEDBACK_START_DELAY);
+ mStatusBar.postStartSettingsActivity(intent, mFeedbackStartDelay);
}
@Override
@@ -169,4 +173,9 @@
public CastController getCastController() {
return mCast;
}
+
+ @Override
+ public VolumeComponent getVolumeComponent() {
+ return mVolume;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
new file mode 100644
index 0000000..95255d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -0,0 +1,187 @@
+/*
+ * 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.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+/**
+ * Controls both the scrim behind the notifications and in front of the notifications (when a
+ * security method gets shown).
+ */
+public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
+
+ private static final float SCRIM_BEHIND_ALPHA = 0.62f;
+ private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.5f;
+ private static final float SCRIM_IN_FRONT_ALPHA = 0.75f;
+ private static final long ANIMATION_DURATION = 220;
+
+ private final View mScrimBehind;
+ private final View mScrimInFront;
+ private final UnlockMethodCache mUnlockMethodCache;
+
+ private boolean mKeyguardShowing;
+ private float mFraction;
+
+ private boolean mDarkenWhileDragging;
+ private boolean mBouncerShowing;
+ private boolean mAnimateChange;
+ private boolean mUpdatePending;
+
+ private final Interpolator mInterpolator = new DecelerateInterpolator();
+
+ public ScrimController(View scrimBehind, View scrimInFront) {
+ mScrimBehind = scrimBehind;
+ mScrimInFront = scrimInFront;
+ mUnlockMethodCache = UnlockMethodCache.getInstance(scrimBehind.getContext());
+ }
+
+ public void setKeyguardShowing(boolean showing) {
+ mKeyguardShowing = showing;
+ scheduleUpdate();
+ }
+
+ public void onTrackingStarted() {
+ mDarkenWhileDragging = !mUnlockMethodCache.isMethodInsecure();
+ }
+
+ public void setPanelExpansion(float fraction) {
+ mFraction = fraction;
+ scheduleUpdate();
+ }
+
+ public void setBouncerShowing(boolean showing) {
+ mBouncerShowing = showing;
+ mAnimateChange = true;
+ scheduleUpdate();
+ }
+
+ private void scheduleUpdate() {
+ if (mUpdatePending) return;
+ mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this);
+ mUpdatePending = true;
+ }
+
+ private void updateScrims() {
+ if (!mKeyguardShowing) {
+ updateScrimNormal();
+ setScrimInFrontColor(0);
+ } else {
+ updateScrimKeyguard();
+ }
+ mAnimateChange = false;
+ }
+
+ private void updateScrimKeyguard() {
+ if (mBouncerShowing) {
+ setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA);
+ setScrimBehindColor(0f);
+ } else if (mDarkenWhileDragging) {
+ float behindFraction = Math.max(0, Math.min(mFraction, 1));
+ float fraction = 1 - behindFraction;
+ setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA);
+ setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD);
+ } else {
+ setScrimInFrontColor(0f);
+ setScrimBehindColor(SCRIM_BEHIND_ALPHA_KEYGUARD);
+ }
+ }
+
+ private void updateScrimNormal() {
+ float frac = mFraction;
+ // let's start this 20% of the way down the screen
+ frac = frac * 1.2f - 0.2f;
+ if (frac <= 0) {
+ setScrimBehindColor(0);
+ } else {
+ // woo, special effects
+ final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
+ setScrimBehindColor(k * SCRIM_BEHIND_ALPHA);
+ }
+ }
+
+ private void setScrimBehindColor(float alpha) {
+ setScrimColor(mScrimBehind, alpha);
+ }
+
+ private void setScrimInFrontColor(float alpha) {
+ setScrimColor(mScrimInFront, alpha);
+ if (alpha == 0f) {
+ mScrimInFront.setClickable(false);
+ } else {
+
+ // Eat touch events.
+ mScrimInFront.setClickable(true);
+ }
+ }
+
+ private void setScrimColor(View scrim, float alpha) {
+ int color = Color.argb((int) (alpha * 255), 0, 0, 0);
+ if (mAnimateChange) {
+ startScrimAnimation(scrim, color);
+ } else {
+ scrim.setBackgroundColor(color);
+ }
+ }
+
+ private void startScrimAnimation(final View scrim, int targetColor) {
+ int current = getBackgroundAlpha(scrim);
+ int target = Color.alpha(targetColor);
+ if (current == targetColor) {
+ return;
+ }
+ ValueAnimator anim = ValueAnimator.ofInt(current, target);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int value = (int) animation.getAnimatedValue();
+ scrim.setBackgroundColor(Color.argb(value, 0, 0, 0));
+ }
+ });
+ anim.setInterpolator(mInterpolator);
+ anim.setDuration(ANIMATION_DURATION);
+ anim.start();
+ }
+
+ private int getBackgroundAlpha(View scrim) {
+ if (scrim.getBackground() instanceof ColorDrawable) {
+ ColorDrawable drawable = (ColorDrawable) scrim.getBackground();
+ return Color.alpha(drawable.getColor());
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
+ mUpdatePending = false;
+ updateScrims();
+ return true;
+ }
+}
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 389e725..3245f1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -38,6 +38,11 @@
*/
public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener {
+ /**
+ * How much the header expansion gets rubberbanded while expanding the panel.
+ */
+ private static final float EXPANSION_RUBBERBAND_FACTOR = 0.35f;
+
private boolean mExpanded;
private boolean mKeyguardShowing;
@@ -128,6 +133,8 @@
updateVisibilities();
updateSystemIconsLayoutParams();
updateBrightnessControllerState();
+ updateZTranslation();
+ updateClickTargets();
if (mQSPanel != null) {
mQSPanel.setExpanded(expanded);
}
@@ -202,18 +209,30 @@
}
}
+ private void updateClickTargets() {
+ mDateTime.setClickable(mExpanded);
+ mMultiUserSwitch.setClickable(mExpanded);
+ }
+
+ private void updateZTranslation() {
+
+ // If we are on the Keyguard, we need to set our z position to zero, so we don't get
+ // shadows.
+ if (mKeyguardShowing && !mExpanded) {
+ setZ(0);
+ } else {
+ setTranslationZ(0);
+ }
+ }
+
public void setExpansion(float height) {
+ height = (height - mCollapsedHeight) * EXPANSION_RUBBERBAND_FACTOR + mCollapsedHeight;
if (height < mCollapsedHeight) {
height = mCollapsedHeight;
}
if (height > mExpandedHeight) {
height = mExpandedHeight;
}
- if (mExpanded) {
- mBackground.setTranslationY(-(mExpandedHeight - height));
- } else {
- mBackground.setTranslationY(0);
- }
setClipping(height);
}
@@ -247,14 +266,10 @@
public void setKeyguardShowing(boolean keyguardShowing) {
mKeyguardShowing = keyguardShowing;
- if (keyguardShowing) {
- setZ(0);
- } else {
- setTranslationZ(0);
- }
updateHeights();
updateWidth();
updateVisibilities();
+ updateZTranslation();
}
public void setUserInfoController(UserInfoController userInfoController) {
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 3849d8d..c3430c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -45,6 +45,7 @@
private LockPatternUtils mLockPatternUtils;
private ViewMediatorCallback mViewMediatorCallback;
private PhoneStatusBar mPhoneStatusBar;
+ private ScrimController mScrimController;
private ViewGroup mContainer;
private StatusBarWindowManager mStatusBarWindowManager;
@@ -68,10 +69,12 @@
}
public void registerStatusBar(PhoneStatusBar phoneStatusBar,
- ViewGroup container, StatusBarWindowManager statusBarWindowManager) {
+ ViewGroup container, StatusBarWindowManager statusBarWindowManager,
+ ScrimController scrimController) {
mPhoneStatusBar = phoneStatusBar;
mContainer = container;
mStatusBarWindowManager = statusBarWindowManager;
+ mScrimController = scrimController;
mBouncer = new KeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils,
mStatusBarWindowManager, container);
}
@@ -98,8 +101,7 @@
mBouncer.show();
} else {
mPhoneStatusBar.showKeyguard();
- mBouncer.hide();
- mBouncer.prepare();
+ mBouncer.hide(false /* destroyView */);
}
}
@@ -124,7 +126,7 @@
if (mShowing) {
if (mOccluded) {
mPhoneStatusBar.hideKeyguard();
- mBouncer.hide();
+ mBouncer.hide(false /* destroyView */);
} else {
showBouncerOrKeyguard();
}
@@ -184,7 +186,7 @@
mShowing = false;
mPhoneStatusBar.hideKeyguard();
mStatusBarWindowManager.setKeyguardShowing(false);
- mBouncer.hide();
+ mBouncer.hide(true /* destroyView */);
mViewMediatorCallback.keyguardGone();
updateStates();
}
@@ -216,7 +218,7 @@
*/
public boolean onBackPressed() {
if (mBouncer.isShowing()) {
- mBouncer.hide();
+ mBouncer.hide(false /* destroyView */);
mPhoneStatusBar.showKeyguard();
updateStates();
return true;
@@ -253,6 +255,7 @@
if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
mStatusBarWindowManager.setBouncerShowing(bouncerShowing);
mPhoneStatusBar.setBouncerShowing(bouncerShowing);
+ mScrimController.setBouncerShowing(bouncerShowing);
}
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
new file mode 100644
index 0000000..bfd657b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+
+import java.util.ArrayList;
+
+/**
+ * Caches whether the current unlock method is insecure, taking trust into account. This information
+ * might be a little bit out of date and should not be used for actual security decisions; it should
+ * be only used for visual indications.
+ */
+public class UnlockMethodCache {
+
+ private static UnlockMethodCache sInstance;
+
+ private final LockPatternUtils mLockPatternUtils;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final ArrayList<OnUnlockMethodChangedListener> mListeners = new ArrayList<>();
+ private boolean mMethodInsecure;
+
+ private UnlockMethodCache(Context ctx) {
+ mLockPatternUtils = new LockPatternUtils(ctx);
+ mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(ctx);
+ KeyguardUpdateMonitor.getInstance(ctx).registerCallback(mCallback);
+ updateMethodSecure(true /* updateAlways */);
+ }
+
+ public static UnlockMethodCache getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new UnlockMethodCache(context);
+ }
+ return sInstance;
+ }
+
+ /**
+ * @return whether the current security method is secure, i. e. the bouncer will be shown
+ */
+ public boolean isMethodInsecure() {
+ return mMethodInsecure;
+ }
+
+ public void addListener(OnUnlockMethodChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ public void removeListener(OnUnlockMethodChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ private void updateMethodSecure(boolean updateAlways) {
+ int user = mLockPatternUtils.getCurrentUser();
+ boolean methodInsecure = !mLockPatternUtils.isSecure() ||
+ mKeyguardUpdateMonitor.getUserHasTrust(user);
+ boolean changed = methodInsecure != mMethodInsecure;
+ if (changed || updateAlways) {
+ mMethodInsecure = methodInsecure;
+ notifyListeners(mMethodInsecure);
+ }
+ }
+
+ private void notifyListeners(boolean secure) {
+ for (OnUnlockMethodChangedListener listener : mListeners) {
+ listener.onMethodSecureChanged(secure);
+ }
+ }
+
+ private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onUserSwitchComplete(int userId) {
+ updateMethodSecure(false /* updateAlways */);
+ }
+
+ @Override
+ public void onTrustChanged(int userId) {
+ updateMethodSecure(false /* updateAlways */);
+ }
+
+ @Override
+ public void onScreenTurnedOn() {
+ updateMethodSecure(false /* updateAlways */);
+ }
+ };
+
+ public static interface OnUnlockMethodChangedListener {
+ void onMethodSecureChanged(boolean methodSecure);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index 81e2cb3..9271e71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Outline;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
@@ -25,6 +26,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import com.android.systemui.ExpandHelper;
@@ -35,14 +37,17 @@
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.NotificationData;
-public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.Callback, ExpandHelper.Callback {
+public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.Callback, ExpandHelper.Callback,
+ ViewTreeObserver.OnComputeInternalInsetsListener {
private static final String TAG = "HeadsUpNotificationView";
private static final boolean DEBUG = false;
private static final boolean SPEW = DEBUG;
Rect mTmpRect = new Rect();
+ int[] mTmpTwoArray = new int[2];
private final int mTouchSensitivityDelay;
+ private final float mMaxAlpha = 0.95f;
private SwipeHelper mSwipeHelper;
private EdgeSwipeHelper mEdgeSwipeHelper;
@@ -87,8 +92,9 @@
}
mContentHolder.setX(0);
mContentHolder.setVisibility(View.VISIBLE);
- mContentHolder.setAlpha(1f);
+ mContentHolder.setAlpha(mMaxAlpha);
mContentHolder.addView(mHeadsUp.row);
+
mSwipeHelper.snapChild(mContentHolder, 1f);
mStartTouchTime = System.currentTimeMillis() + mTouchSensitivityDelay;
}
@@ -99,32 +105,6 @@
return mHeadsUp == null || mHeadsUp.notification.isClearable();
}
- public void setMargin(int notificationPanelMarginPx) {
- if (SPEW) Log.v(TAG, "setMargin() " + notificationPanelMarginPx);
- if (mContentHolder != null &&
- mContentHolder.getLayoutParams() instanceof FrameLayout.LayoutParams) {
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mContentHolder.getLayoutParams();
- lp.setMarginStart(notificationPanelMarginPx);
- mContentHolder.setLayoutParams(lp);
- }
- }
-
- // LinearLayout methods
-
- @Override
- public void onDraw(android.graphics.Canvas c) {
- super.onDraw(c);
- if (DEBUG) {
- //Log.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
- // + getMeasuredHeight() + "px");
- c.save();
- c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6,
- android.graphics.Region.Op.DIFFERENCE);
- c.drawColor(0xFFcc00cc);
- c.restore();
- }
- }
-
// ViewGroup methods
@Override
@@ -134,6 +114,7 @@
float pagingTouchSlop = viewConfiguration.getScaledPagingTouchSlop();
float touchSlop = viewConfiguration.getScaledTouchSlop();
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
+ mSwipeHelper.setMaxAlpha(mMaxAlpha);
mEdgeSwipeHelper = new EdgeSwipeHelper(touchSlop);
int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
@@ -146,6 +127,8 @@
// whoops, we're on already!
setNotification(mHeadsUp);
}
+
+ getViewTreeObserver().addOnComputeInternalInsetsListener(this);
}
@Override
@@ -163,6 +146,20 @@
// View methods
@Override
+ public void onDraw(android.graphics.Canvas c) {
+ super.onDraw(c);
+ if (DEBUG) {
+ //Log.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
+ // + getMeasuredHeight() + "px");
+ c.save();
+ c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6,
+ android.graphics.Region.Op.DIFFERENCE);
+ c.drawColor(0xFFcc00cc);
+ c.restore();
+ }
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
if (System.currentTimeMillis() < mStartTouchTime) {
return false;
@@ -183,6 +180,14 @@
mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
}
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ Outline o = new Outline();
+ o.setRect(0, 0, mContentHolder.getWidth(), mContentHolder.getHeight());
+ mContentHolder.setOutline(o);
+ }
+
// ExpandHelper.Callback methods
@Override
@@ -233,7 +238,7 @@
@Override
public void onDragCancelled(View v) {
- mContentHolder.setAlpha(1f); // sometimes this isn't quite reset
+ mContentHolder.setAlpha(mMaxAlpha); // sometimes this isn't quite reset
}
@Override
@@ -250,6 +255,16 @@
return mContentHolder;
}
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+ mContentHolder.getLocationOnScreen(mTmpTwoArray);
+
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ info.touchableRegion.set(mTmpTwoArray[0], mTmpTwoArray[1],
+ mTmpTwoArray[0] + mContentHolder.getWidth(),
+ mTmpTwoArray[1] + mContentHolder.getHeight());
+ }
+
private class EdgeSwipeHelper implements Gefingerpoken {
private static final boolean DEBUG_EDGE_SWIPE = false;
private final float mTouchSlop;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 718acc3..330b599 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -17,12 +17,9 @@
package com.android.systemui.statusbar.policy;
import android.animation.Animator;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.hardware.input.InputManager;
import android.os.SystemClock;
@@ -34,9 +31,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
-import android.view.View;
import android.view.ViewConfiguration;
-import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
@@ -46,25 +41,18 @@
private static final String TAG = "StatusBar.KeyButtonView";
private static final boolean DEBUG = false;
- final float GLOW_MAX_SCALE_FACTOR = 1.8f;
public static final float DEFAULT_QUIESCENT_ALPHA = 0.70f;
- long mDownTime;
- int mCode;
- int mTouchSlop;
- Drawable mGlowBG;
- int mGlowWidth, mGlowHeight;
- float mGlowAlpha = 0f, mGlowScale = 1f;
- @ViewDebug.ExportedProperty(category = "drawing")
- float mDrawingAlpha = 1f;
- @ViewDebug.ExportedProperty(category = "drawing")
- float mQuiescentAlpha = DEFAULT_QUIESCENT_ALPHA;
- boolean mSupportsLongpress = true;
- RectF mRect = new RectF();
- AnimatorSet mPressedAnim;
- Animator mAnimateToQuiescent = new ObjectAnimator();
+ private long mDownTime;
+ private int mCode;
+ private int mTouchSlop;
+ private float mDrawingAlpha = 1f;
+ private float mQuiescentAlpha = DEFAULT_QUIESCENT_ALPHA;
+ private boolean mSupportsLongpress = true;
+ private Animator mAnimateToQuiescent = new ObjectAnimator();
+ private Drawable mBackground;
- Runnable mCheckLongPress = new Runnable() {
+ private final Runnable mCheckLongPress = new Runnable() {
public void run() {
if (isPressed()) {
// Log.d("KeyButtonView", "longpressed: " + this);
@@ -93,47 +81,27 @@
mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
- mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground);
- setDrawingAlpha(mQuiescentAlpha);
- if (mGlowBG != null) {
- mGlowWidth = mGlowBG.getIntrinsicWidth();
- mGlowHeight = mGlowBG.getIntrinsicHeight();
+ Drawable d = getBackground();
+ if (d != null) {
+ mBackground = d.mutate();
+ setBackground(mBackground);
}
+ setDrawingAlpha(mQuiescentAlpha);
+
a.recycle();
setClickable(true);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
- @Override
- protected void onDraw(Canvas canvas) {
- if (mGlowBG != null) {
- canvas.save();
- final int w = getWidth();
- final int h = getHeight();
- final float aspect = (float)mGlowWidth / mGlowHeight;
- final int drawW = (int)(h*aspect);
- final int drawH = h;
- final int margin = (drawW-w)/2;
- canvas.scale(mGlowScale, mGlowScale, w*0.5f, h*0.5f);
- mGlowBG.setBounds(-margin, 0, drawW-margin, drawH);
- mGlowBG.setAlpha((int)(mDrawingAlpha * mGlowAlpha * 255));
- mGlowBG.draw(canvas);
- canvas.restore();
- mRect.right = w;
- mRect.bottom = h;
- }
- super.onDraw(canvas);
- }
-
public void setQuiescentAlpha(float alpha, boolean animate) {
mAnimateToQuiescent.cancel();
alpha = Math.min(Math.max(alpha, 0), 1);
if (alpha == mQuiescentAlpha && alpha == mDrawingAlpha) return;
mQuiescentAlpha = alpha;
if (DEBUG) Log.d(TAG, "New quiescent alpha = " + mQuiescentAlpha);
- if (mGlowBG != null && animate) {
+ if (mBackground != null && animate) {
mAnimateToQuiescent = animateToQuiescent();
mAnimateToQuiescent.start();
} else {
@@ -154,87 +122,35 @@
}
public void setDrawingAlpha(float x) {
- // Calling setAlpha(int), which is an ImageView-specific
- // method that's different from setAlpha(float). This sets
- // the alpha on this ImageView's drawable directly
- setAlpha((int) (x * 255));
+ setImageAlpha((int) (x * 255));
+ if (mBackground != null) {
+ mBackground.setAlpha((int)(x * 255));
+ }
mDrawingAlpha = x;
}
- public float getGlowAlpha() {
- if (mGlowBG == null) return 0;
- return mGlowAlpha;
- }
-
- public void setGlowAlpha(float x) {
- if (mGlowBG == null) return;
- mGlowAlpha = x;
- invalidate();
- }
-
- public float getGlowScale() {
- if (mGlowBG == null) return 0;
- return mGlowScale;
- }
-
- public void setGlowScale(float x) {
- if (mGlowBG == null) return;
- mGlowScale = x;
- final float w = getWidth();
- final float h = getHeight();
- if (GLOW_MAX_SCALE_FACTOR <= 1.0f) {
- // this only works if we know the glow will never leave our bounds
- invalidate();
- } else {
- final float rx = (w * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
- final float ry = (h * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
- com.android.systemui.SwipeHelper.invalidateGlobalRegion(
- this,
- new RectF(getLeft() - rx,
- getTop() - ry,
- getRight() + rx,
- getBottom() + ry));
-
- // also invalidate our immediate parent to help avoid situations where nearby glows
- // interfere
- ((View)getParent()).invalidate();
- }
- }
-
public void setPressed(boolean pressed) {
- if (mGlowBG != null) {
+ if (mBackground != null) {
if (pressed != isPressed()) {
- if (mPressedAnim != null && mPressedAnim.isRunning()) {
- mPressedAnim.cancel();
- }
- final AnimatorSet as = mPressedAnim = new AnimatorSet();
if (pressed) {
- if (mGlowScale < GLOW_MAX_SCALE_FACTOR)
- mGlowScale = GLOW_MAX_SCALE_FACTOR;
- if (mGlowAlpha < mQuiescentAlpha)
- mGlowAlpha = mQuiescentAlpha;
setDrawingAlpha(1f);
- as.playTogether(
- ObjectAnimator.ofFloat(this, "glowAlpha", 1f),
- ObjectAnimator.ofFloat(this, "glowScale", GLOW_MAX_SCALE_FACTOR)
- );
- as.setDuration(50);
} else {
mAnimateToQuiescent.cancel();
mAnimateToQuiescent = animateToQuiescent();
- as.playTogether(
- ObjectAnimator.ofFloat(this, "glowAlpha", 0f),
- ObjectAnimator.ofFloat(this, "glowScale", 1f),
- mAnimateToQuiescent
- );
- as.setDuration(500);
+ mAnimateToQuiescent.setDuration(500);
+ mAnimateToQuiescent.start();
}
- as.start();
}
}
super.setPressed(pressed);
}
+ private void setHotspot(float x, float y) {
+ if (mBackground != null) {
+ mBackground.setHotspot(x, y);
+ }
+ }
+
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
int x, y;
@@ -254,6 +170,7 @@
removeCallbacks(mCheckLongPress);
postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
}
+ setHotspot(ev.getX(), ev.getY());
break;
case MotionEvent.ACTION_MOVE:
x = (int)ev.getX();
@@ -262,6 +179,7 @@
&& x < getWidth() + mTouchSlop
&& y >= -mTouchSlop
&& y < getHeight() + mTouchSlop);
+ setHotspot(ev.getX(), ev.getY());
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
index 173af40..3ce6905 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -166,7 +166,7 @@
if (rawAvatar != null) {
avatar = new BitmapDrawable(mContext.getResources(), circularClip(rawAvatar));
} else {
- avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user);
+ avatar = mContext.getResources().getDrawable(R.drawable.ic_account_circle);
mUseDefaultAvatar = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 41914ed..5e2d06b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -28,6 +28,7 @@
boolean animateScale;
boolean animateHeight;
boolean animateDimmed;
+ boolean hasDelays;
public AnimationFilter animateAlpha() {
animateAlpha = true;
@@ -39,6 +40,11 @@
return this;
}
+ public AnimationFilter hasDelays() {
+ hasDelays = true;
+ return this;
+ }
+
public AnimationFilter animateZ() {
animateZ = true;
return this;
@@ -79,6 +85,7 @@
animateScale |= filter.animateScale;
animateHeight |= filter.animateHeight;
animateDimmed |= filter.animateDimmed;
+ hasDelays |= filter.hasDelays;
}
private void reset() {
@@ -88,5 +95,6 @@
animateScale = false;
animateHeight = false;
animateDimmed = false;
+ hasDelays = false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 90f3d17..079b184 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -103,6 +103,7 @@
private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<View>();
private ArrayList<View> mSnappedBackChildren = new ArrayList<View>();
private ArrayList<View> mDragAnimPendingChildren = new ArrayList<View>();
+ private ArrayList<View> mChildrenChangingPositions = new ArrayList<View>();
private ArrayList<AnimationEvent> mAnimationEvents
= new ArrayList<AnimationEvent>();
private ArrayList<View> mSwipedOutViews = new ArrayList<View>();
@@ -969,9 +970,24 @@
}
/**
+ * @return The first child which has visibility unequal to GONE which is currently below the
+ * given translationY or equal to it.
+ */
+ private View getFirstChildBelowTranlsationY(float translationY) {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != View.GONE && child.getTranslationY() >= translationY) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ /**
* @return the last child which has visibility unequal to GONE
*/
- private View getLastChildNotGone() {
+ public View getLastChildNotGone() {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
@@ -1094,23 +1110,41 @@
@Override
protected void onViewRemoved(View child) {
super.onViewRemoved(child);
+ mStackScrollAlgorithm.notifyChildrenChanged(this);
+ if (mChildrenChangingPositions.contains(child)) {
+ // This is only a position change, don't do anything special
+ return;
+ }
((ExpandableView) child).setOnHeightChangedListener(null);
mCurrentStackScrollState.removeViewStateForView(child);
- mStackScrollAlgorithm.notifyChildrenChanged(this);
updateScrollStateForRemovedChild(child);
- generateRemoveAnimation(child);
+ boolean animationGenerated = generateRemoveAnimation(child);
+ if (animationGenerated && !mSwipedOutViews.contains(child)) {
+ // Add this view to an overlay in order to ensure that it will still be temporary
+ // drawn when removed
+ getOverlay().add(child);
+ }
}
- private void generateRemoveAnimation(View child) {
+ /**
+ * Generate a remove animation for a child view.
+ *
+ * @param child The view to generate the remove animation for.
+ * @return Whether an animation was generated.
+ */
+ private boolean generateRemoveAnimation(View child) {
if (mIsExpanded && mAnimationsEnabled) {
if (!mChildrenToAddAnimated.contains(child)) {
// Generate Animations
mChildrenToRemoveAnimated.add(child);
mNeedsAnimation = true;
+ return true;
} else {
mChildrenToAddAnimated.remove(child);
+ return false;
}
}
+ return false;
}
/**
@@ -1155,9 +1189,7 @@
super.onViewAdded(child);
mStackScrollAlgorithm.notifyChildrenChanged(this);
((ExpandableView) child).setOnHeightChangedListener(this);
- if (child.getVisibility() != View.GONE) {
- generateAddAnimation(child);
- }
+ generateAddAnimation(child);
}
public void setAnimationsEnabled(boolean animationsEnabled) {
@@ -1168,10 +1200,13 @@
return mNeedsAnimation
&& (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
}
-
+ /**
+ * Generate an animation for an added child view.
+ *
+ * @param child The view to be added.
+ */
public void generateAddAnimation(View child) {
- if (mIsExpanded && mAnimationsEnabled) {
-
+ if (mIsExpanded && mAnimationsEnabled && !mChildrenChangingPositions.contains(child)) {
// Generate Animations
mChildrenToAddAnimated.add(child);
mNeedsAnimation = true;
@@ -1186,9 +1221,10 @@
*/
public void changeViewPosition(View child, int newIndex) {
if (child != null && child.getParent() == this) {
+ mChildrenChangingPositions.add(child);
removeView(child);
addView(child, newIndex);
- // TODO: handle events
+ mNeedsAnimation = true;
}
}
@@ -1197,16 +1233,18 @@
generateChildHierarchyEvents();
mNeedsAnimation = false;
}
- if (!mAnimationEvents.isEmpty()) {
+ if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState);
+ mAnimationEvents.clear();
} else {
applyCurrentState();
}
}
private void generateChildHierarchyEvents() {
- generateChildAdditionEvents();
generateChildRemovalEvents();
+ generateChildAdditionEvents();
+ generatePositionChangeEvents();
generateSnapBackEvents();
generateDragEvents();
generateTopPaddingEvent();
@@ -1237,12 +1275,24 @@
int animationType = childWasSwipedOut
? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
: AnimationEvent.ANIMATION_TYPE_REMOVE;
- mAnimationEvents.add(new AnimationEvent(child, animationType));
+ AnimationEvent event = new AnimationEvent(child, animationType);
+
+ // we need to know the view after this one
+ event.viewAfterChangingView = getFirstChildBelowTranlsationY(child.getTranslationY());
+ mAnimationEvents.add(event);
}
mSwipedOutViews.clear();
mChildrenToRemoveAnimated.clear();
}
+ private void generatePositionChangeEvents() {
+ for (View child : mChildrenChangingPositions) {
+ mAnimationEvents.add(new AnimationEvent(child,
+ AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
+ }
+ mChildrenChangingPositions.clear();
+ }
+
private void generateChildAdditionEvents() {
for (View child : mChildrenToAddAnimated) {
mAnimationEvents.add(new AnimationEvent(child,
@@ -1467,7 +1517,6 @@
public void onChildAnimationFinished() {
requestChildrenUpdate();
- mAnimationEvents.clear();
}
/**
@@ -1513,9 +1562,9 @@
}
private void updateSpeedBump(boolean visible) {
- int newVisibility = visible ? VISIBLE : GONE;
- int oldVisibility = mSpeedBumpView.getVisibility();
- if (newVisibility != oldVisibility) {
+ boolean notGoneBefore = mSpeedBumpView.getVisibility() != GONE;
+ if (visible != notGoneBefore) {
+ int newVisibility = visible ? VISIBLE : GONE;
mSpeedBumpView.setVisibility(newVisibility);
if (visible) {
mSpeedBumpView.collapse();
@@ -1551,21 +1600,24 @@
.animateAlpha()
.animateHeight()
.animateY()
- .animateZ(),
+ .animateZ()
+ .hasDelays(),
// ANIMATION_TYPE_REMOVE
new AnimationFilter()
.animateAlpha()
.animateHeight()
.animateY()
- .animateZ(),
+ .animateZ()
+ .hasDelays(),
// ANIMATION_TYPE_REMOVE_SWIPED_OUT
new AnimationFilter()
.animateAlpha()
.animateHeight()
.animateY()
- .animateZ(),
+ .animateZ()
+ .hasDelays(),
// ANIMATION_TYPE_TOP_PADDING_CHANGED
new AnimationFilter()
@@ -1593,16 +1645,23 @@
new AnimationFilter()
.animateY()
.animateScale()
- .animateDimmed()
+ .animateDimmed(),
+
+ // ANIMATION_TYPE_CHANGE_POSITION
+ new AnimationFilter()
+ .animateAlpha()
+ .animateHeight()
+ .animateY()
+ .animateZ()
};
static int[] LENGTHS = new int[] {
// ANIMATION_TYPE_ADD
- StackStateAnimator.ANIMATION_DURATION_STANDARD,
+ StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
// ANIMATION_TYPE_REMOVE
- StackStateAnimator.ANIMATION_DURATION_STANDARD,
+ StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
// ANIMATION_TYPE_REMOVE_SWIPED_OUT
StackStateAnimator.ANIMATION_DURATION_STANDARD,
@@ -1621,22 +1680,27 @@
// ANIMATION_TYPE_DIMMED
StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
+
+ // ANIMATION_TYPE_CHANGE_POSITION
+ StackStateAnimator.ANIMATION_DURATION_STANDARD,
};
- static int ANIMATION_TYPE_ADD = 0;
- static int ANIMATION_TYPE_REMOVE = 1;
- static int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
- static int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
- static int ANIMATION_TYPE_START_DRAG = 4;
- static int ANIMATION_TYPE_SNAP_BACK = 5;
- static int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
- static int ANIMATION_TYPE_DIMMED = 7;
+ static final int ANIMATION_TYPE_ADD = 0;
+ static final int ANIMATION_TYPE_REMOVE = 1;
+ static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
+ static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
+ static final int ANIMATION_TYPE_START_DRAG = 4;
+ static final int ANIMATION_TYPE_SNAP_BACK = 5;
+ static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
+ static final int ANIMATION_TYPE_DIMMED = 7;
+ static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
final long eventStartTime;
final View changingView;
final int animationType;
final AnimationFilter filter;
final long length;
+ View viewAfterChangingView;
AnimationEvent(View view, int type) {
eventStartTime = AnimationUtils.currentAnimationTimeMillis();
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 d572ea5..bd2541a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -208,6 +208,8 @@
for (int i = 0; i < childCount; i++) {
ExpandableView v = (ExpandableView) hostView.getChildAt(i);
if (v.getVisibility() != View.GONE) {
+ StackScrollState.ViewState viewState = resultState.getViewStateForView(v);
+ viewState.notGoneIndex = state.visibleChildren.size();
state.visibleChildren.add(v);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 011411c..44e10be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -37,15 +37,14 @@
private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
private final ViewGroup mHostView;
+ private final int mRoundedRectCornerRadius;
private Map<ExpandableView, ViewState> mStateMap;
private final Rect mClipRect = new Rect();
- private int mBackgroundRoundedRectCornerRadius;
- private final Outline mChildOutline = new Outline();
public StackScrollState(ViewGroup hostView) {
mHostView = hostView;
mStateMap = new HashMap<ExpandableView, ViewState>();
- mBackgroundRoundedRectCornerRadius = hostView.getResources().getDimensionPixelSize(
+ mRoundedRectCornerRadius = mHostView.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
}
@@ -66,6 +65,7 @@
viewState.height = child.getIntrinsicHeight();
viewState.gone = child.getVisibility() == View.GONE;
viewState.alpha = 1;
+ viewState.notGoneIndex = -1;
}
}
@@ -158,11 +158,15 @@
// apply clipping and shadow
float newNotificationEnd = newYTranslation + newHeight;
+ // In the unlocked shade we have to clip a little bit higher because of the rounded
+ // corners of the notifications.
+ float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius;
+
// When the previous notification is swiped, we don't clip the content to the
// bottom of it.
float clipHeight = previousNotificationIsSwiped
? newHeight
- : newNotificationEnd - (previousNotificationEnd);
+ : newNotificationEnd - (previousNotificationEnd - clippingCorrection);
updateChildClippingAndBackground(child, newHeight,
clipHeight,
@@ -190,7 +194,7 @@
if (nextChild != null) {
ViewState nextState = getViewStateForView(nextChild);
boolean startIsAboveNext = nextState.yTranslation > speedBumpStart;
- speedBump.animateDivider(startIsAboveNext);
+ speedBump.animateDivider(startIsAboveNext, null /* onFinishedRunnable */);
// handle expanded case
if (speedBump.isExpanded()) {
@@ -272,6 +276,11 @@
boolean dimmed;
/**
+ * The index of the view, only accounting for views not equal to GONE
+ */
+ int notGoneIndex;
+
+ /**
* The location this view is currently rendered at.
*
* <p>See <code>LOCATION_</code> flags.</p>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index a9dcdd6..f019e6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -39,7 +39,11 @@
public class StackStateAnimator {
public static final int ANIMATION_DURATION_STANDARD = 360;
+ public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
+ public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
+ public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
+ private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
@@ -62,10 +66,9 @@
private final Interpolator mFastOutSlowInInterpolator;
public NotificationStackScrollLayout mHostLayout;
- private ArrayList<NotificationStackScrollLayout.AnimationEvent> mHandledEvents =
- new ArrayList<>();
private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
new ArrayList<>();
+ private ArrayList<View> mNewAddChildren = new ArrayList<>();
private Set<Animator> mAnimatorSet = new HashSet<Animator>();
private Stack<AnimatorListenerAdapter> mAnimationListenerPool
= new Stack<AnimatorListenerAdapter>();
@@ -96,57 +99,130 @@
mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
for (int i = 0; i < childCount; i++) {
final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
+
StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
- if (viewState == null) {
+ if (viewState == null || child.getVisibility() == View.GONE) {
continue;
}
- startAnimations(child, viewState);
-
child.setClipBounds(null);
+ startAnimations(child, viewState, finalState);
}
if (!isRunning()) {
// no child has preformed any animation, lets finish
onAnimationFinished();
}
+ mNewEvents.clear();
+ mNewAddChildren.clear();
}
/**
* Start an animation to the given viewState
*/
- private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState) {
+ private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState,
+ StackScrollState finalState) {
int childVisibility = child.getVisibility();
boolean wasVisible = childVisibility == View.VISIBLE;
final float alpha = viewState.alpha;
if (!wasVisible && alpha != 0 && !viewState.gone) {
child.setVisibility(View.VISIBLE);
}
+
+ boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
+ boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation;
+ boolean scaleChanging = child.getScaleX() != viewState.scale;
+ boolean alphaChanging = alpha != child.getAlpha();
+ boolean heightChanging = viewState.height != child.getActualHeight();
+ boolean wasAdded = mNewAddChildren.contains(child);
+ boolean hasDelays = mAnimationFilter.hasDelays;
+ boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
+ alphaChanging || heightChanging;
+ long delay = 0;
+ if (hasDelays && isDelayRelevant || wasAdded) {
+ delay = calculateChildAnimationDelay(viewState, finalState);
+ }
+
// start translationY animation
- if (child.getTranslationY() != viewState.yTranslation) {
- startYTranslationAnimation(child, viewState);
+ if (yTranslationChanging) {
+ startYTranslationAnimation(child, viewState, delay);
}
+
// start translationZ animation
- if (child.getTranslationZ() != viewState.zTranslation) {
- startZTranslationAnimation(child, viewState);
+ if (zTranslationChanging) {
+ startZTranslationAnimation(child, viewState, delay);
}
+
// start scale animation
- if (child.getScaleX() != viewState.scale) {
+ if (scaleChanging) {
startScaleAnimation(child, viewState);
}
+
// start alpha animation
- if (alpha != child.getAlpha()) {
- startAlphaAnimation(child, viewState);
+ if (alphaChanging) {
+ startAlphaAnimation(child, viewState, delay);
}
+
// start height animation
- if (viewState.height != child.getActualHeight()) {
- startHeightAnimation(child, viewState);
+ if (heightChanging) {
+ startHeightAnimation(child, viewState, delay);
}
+
// start dimmed animation
child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
+
+ if (wasAdded) {
+ child.performAddAnimation(delay);
+ }
+ }
+
+ private long calculateChildAnimationDelay(StackScrollState.ViewState viewState,
+ StackScrollState finalState) {
+ long minDelay = 0;
+ for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
+ long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING;
+ switch (event.animationType) {
+ case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: {
+ int ownIndex = viewState.notGoneIndex;
+ int changingIndex = finalState
+ .getViewStateForView(event.changingView).notGoneIndex;
+ int difference = Math.abs(ownIndex - changingIndex);
+ difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
+ difference - 1));
+ long delay = (DELAY_EFFECT_MAX_INDEX_DIFFERENCE - difference) * delayPerElement;
+ minDelay = Math.max(delay, minDelay);
+ break;
+ }
+ case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT:
+ delayPerElement = ANIMATION_DELAY_PER_ELEMENT_MANUAL;
+ case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: {
+ int ownIndex = viewState.notGoneIndex;
+ boolean noNextView = event.viewAfterChangingView == null;
+ View viewAfterChangingView = noNextView
+ ? mHostLayout.getLastChildNotGone()
+ : event.viewAfterChangingView;
+
+ int nextIndex = finalState
+ .getViewStateForView(viewAfterChangingView).notGoneIndex;
+ if (ownIndex >= nextIndex) {
+ // we only have the view afterwards
+ ownIndex++;
+ }
+ int difference = Math.abs(ownIndex - nextIndex);
+ difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
+ difference - 1));
+ long delay = difference * delayPerElement;
+ minDelay = Math.max(delay, minDelay);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return minDelay;
}
private void startHeightAnimation(final ExpandableView child,
- StackScrollState.ViewState viewState) {
+ StackScrollState.ViewState viewState, long delay) {
Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
int newEndValue = viewState.height;
@@ -185,6 +261,9 @@
animator.setInterpolator(mFastOutSlowInInterpolator);
long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
animator.setDuration(newDuration);
+ if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
+ animator.setStartDelay(delay);
+ }
animator.addListener(getGlobalAnimationFinishedListener());
// remove the tag when the animation is finished
animator.addListener(new AnimatorListenerAdapter() {
@@ -195,14 +274,14 @@
child.setTag(TAG_END_HEIGHT, null);
}
});
- startInstantly(animator);
+ startAnimator(animator);
child.setTag(TAG_ANIMATOR_HEIGHT, animator);
child.setTag(TAG_START_HEIGHT, child.getActualHeight());
child.setTag(TAG_END_HEIGHT, newEndValue);
}
private void startAlphaAnimation(final ExpandableView child,
- final StackScrollState.ViewState viewState) {
+ final StackScrollState.ViewState viewState, long delay) {
Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
final float newEndValue = viewState.alpha;
@@ -236,14 +315,13 @@
child.getAlpha(), newEndValue);
animator.setInterpolator(mFastOutSlowInInterpolator);
// Handle layer type
- final int currentLayerType = child.getLayerType();
child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
animator.addListener(new AnimatorListenerAdapter() {
public boolean mWasCancelled;
@Override
public void onAnimationEnd(Animator animation) {
- child.setLayerType(currentLayerType, null);
+ child.setLayerType(View.LAYER_TYPE_NONE, null);
if (newEndValue == 0 && !mWasCancelled) {
child.setVisibility(View.INVISIBLE);
}
@@ -264,6 +342,9 @@
});
long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
animator.setDuration(newDuration);
+ if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
+ animator.setStartDelay(delay);
+ }
animator.addListener(getGlobalAnimationFinishedListener());
// remove the tag when the animation is finished
animator.addListener(new AnimatorListenerAdapter() {
@@ -272,14 +353,14 @@
}
});
- startInstantly(animator);
+ startAnimator(animator);
child.setTag(TAG_ANIMATOR_ALPHA, animator);
child.setTag(TAG_START_ALPHA, child.getAlpha());
child.setTag(TAG_END_ALPHA, newEndValue);
}
private void startZTranslationAnimation(final ExpandableView child,
- final StackScrollState.ViewState viewState) {
+ final StackScrollState.ViewState viewState, long delay) {
Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
float newEndValue = viewState.zTranslation;
@@ -311,6 +392,9 @@
animator.setInterpolator(mFastOutSlowInInterpolator);
long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
animator.setDuration(newDuration);
+ if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
+ animator.setStartDelay(delay);
+ }
animator.addListener(getGlobalAnimationFinishedListener());
// remove the tag when the animation is finished
animator.addListener(new AnimatorListenerAdapter() {
@@ -321,14 +405,14 @@
child.setTag(TAG_END_TRANSLATION_Z, null);
}
});
- startInstantly(animator);
+ startAnimator(animator);
child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ());
child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
}
private void startYTranslationAnimation(final ExpandableView child,
- StackScrollState.ViewState viewState) {
+ StackScrollState.ViewState viewState, long delay) {
Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
float newEndValue = viewState.yTranslation;
@@ -361,6 +445,9 @@
animator.setInterpolator(mFastOutSlowInInterpolator);
long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
animator.setDuration(newDuration);
+ if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
+ animator.setStartDelay(delay);
+ }
animator.addListener(getGlobalAnimationFinishedListener());
// remove the tag when the animation is finished
animator.addListener(new AnimatorListenerAdapter() {
@@ -371,7 +458,7 @@
child.setTag(TAG_END_TRANSLATION_Y, null);
}
});
- startInstantly(animator);
+ startAnimator(animator);
child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY());
child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
@@ -425,18 +512,15 @@
child.setTag(TAG_END_SCALE, null);
}
});
- startInstantly(animator);
+ startAnimator(animator);
child.setTag(TAG_ANIMATOR_SCALE, animator);
child.setTag(TAG_START_SCALE, child.getScaleX());
child.setTag(TAG_END_SCALE, newEndValue);
}
- /**
- * Start an animator instantly instead of waiting on the next synchronization frame
- */
- public static void startInstantly(ValueAnimator animator) {
+ private void startAnimator(ValueAnimator animator) {
+ mAnimatorSet.add(animator);
animator.start();
- animator.setCurrentPlayTime(0);
}
/**
@@ -468,7 +552,6 @@
@Override
public void onAnimationStart(Animator animation) {
- mAnimatorSet.add(animation);
mWasCancelled = false;
}
};
@@ -497,8 +580,6 @@
}
private void onAnimationFinished() {
- mHandledEvents.clear();
- mNewEvents.clear();
mHostLayout.onChildAnimationFinished();
}
@@ -511,27 +592,60 @@
private void processAnimationEvents(
ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
StackScrollState finalState) {
- mNewEvents.clear();
for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
- View changingView = event.changingView;
- if (!mHandledEvents.contains(event)) {
- if (event.animationType == NotificationStackScrollLayout.AnimationEvent
- .ANIMATION_TYPE_ADD) {
+ final ExpandableView changingView = (ExpandableView) event.changingView;
+ if (event.animationType ==
+ NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
- // This item is added, initialize it's properties.
- StackScrollState.ViewState viewState = finalState
- .getViewStateForView(changingView);
- if (viewState == null) {
- // The position for this child was never generated, let's continue.
- continue;
- }
- changingView.setAlpha(0);
- changingView.setTranslationY(viewState.yTranslation);
- changingView.setTranslationZ(viewState.zTranslation);
+ // This item is added, initialize it's properties.
+ StackScrollState.ViewState viewState = finalState
+ .getViewStateForView(changingView);
+ if (viewState == null) {
+ // The position for this child was never generated, let's continue.
+ continue;
}
- mHandledEvents.add(event);
- mNewEvents.add(event);
+ if (changingView.getVisibility() == View.GONE) {
+ // The view was set to gone but the state never removed
+ finalState.removeViewStateForView(changingView);
+ continue;
+ }
+ changingView.setAlpha(viewState.alpha);
+ changingView.setTranslationY(viewState.yTranslation);
+ changingView.setTranslationZ(viewState.zTranslation);
+ changingView.setActualHeight(viewState.height, false);
+ mNewAddChildren.add(changingView);
+
+ } else if (event.animationType ==
+ NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
+ if (changingView.getVisibility() == View.GONE) {
+ continue;
+ }
+
+ // Find the amount to translate up. This is needed in order to understand the
+ // direction of the remove animation (either downwards or upwards)
+ StackScrollState.ViewState viewState = finalState
+ .getViewStateForView(event.viewAfterChangingView);
+ int actualHeight = changingView.getActualHeight();
+ // upwards by default
+ float translationDirection = -1.0f;
+ if (viewState != null) {
+ // there was a view after this one, Approximate the distance the next child
+ // travelled
+ translationDirection = ((viewState.yTranslation
+ - (changingView.getTranslationY() + actualHeight / 2.0f)) * 2 /
+ actualHeight);
+ translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
+
+ }
+ changingView.performRemoveAnimation(translationDirection, new Runnable() {
+ @Override
+ public void run() {
+ // remove the temporary overlay
+ mHostLayout.getOverlay().remove(changingView);
+ }
+ });
}
+ mNewEvents.add(event);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 846d248..25147b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -50,10 +50,18 @@
}
@Override
+ public void addNotificationInternal(StatusBarNotification notification) {
+ }
+
+ @Override
public void updateNotification(StatusBarNotification notification) {
}
@Override
+ protected void removeNotificationInternal(String key) {
+ }
+
+ @Override
public void removeNotification(String key) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
new file mode 100644
index 0000000..5ee89253
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
@@ -0,0 +1,24 @@
+/*
+ * 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.volume;
+
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+public interface VolumeComponent {
+ ZenModeController getZenController();
+ void setVolumePanel(VolumePanel panel);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 8657e07..06f4c2e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -16,14 +16,12 @@
package com.android.systemui.volume;
-import com.android.internal.R;
-
import android.app.AlertDialog;
import android.app.Dialog;
-import android.content.DialogInterface.OnDismissListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
@@ -42,6 +40,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewStub;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
@@ -49,6 +48,9 @@
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
+import com.android.internal.R;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
import java.util.HashMap;
/**
@@ -57,7 +59,6 @@
* @hide
*/
public class VolumePanel extends Handler {
- private static final String TAG = VolumePanel.class.getSimpleName();
private static boolean LOGD = false;
private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
@@ -88,33 +89,48 @@
private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
+ private static final int MSG_LAYOUT_DIRECTION = 12;
+ private static final int MSG_ZEN_MODE_CHANGED = 13;
// Pseudo stream type for master volume
private static final int STREAM_MASTER = -100;
// Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
+ private final String mTag;
protected final Context mContext;
private final AudioManager mAudioManager;
+ private final ZenModeController mZenController;
private boolean mRingIsSilent;
- private boolean mShowCombinedVolumes;
private boolean mVoiceCapable;
+ private boolean mZenModeCapable;
// True if we want to play tones on the system stream when the master stream is specified.
private final boolean mPlayMasterStreamTones;
- /** Dialog containing all the sliders */
- private final Dialog mDialog;
- /** Dialog's content view */
+
+ /** Volume panel content view */
private final View mView;
+ /** Dialog hosting the panel, if not embedded */
+ private final Dialog mDialog;
+ /** Parent view hosting the panel, if embedded */
+ private final ViewGroup mParent;
/** The visible portion of the volume overlay */
private final ViewGroup mPanel;
- /** Contains the sliders and their touchable icons */
- private final ViewGroup mSliderGroup;
- /** The button that expands the dialog to show all sliders */
- private final View mMoreButton;
- /** Dummy divider icon that needs to vanish with the more button */
- private final View mDivider;
+ /** Contains the slider and its touchable icons */
+ private final ViewGroup mSliderPanel;
+ /** The button that expands the dialog to show the zen panel */
+ private final ImageView mExpandButton;
+ /** Dummy divider icon that needs to vanish with the expand button */
+ private final View mExpandDivider;
+ /** The zen mode configuration panel view stub */
+ private final ViewStub mZenPanelStub;
+ /** The zen mode configuration panel view, once inflated */
+ private ZenModePanel mZenPanel;
+ /** Dummy divider icon that needs to vanish with the zen panel */
+ private final View mZenPanelDivider;
+
+ private ZenModePanel.Callback mZenPanelCallback;
/** Currently active stream that shows up at the top of the list of sliders */
private int mActiveStreamType = -1;
@@ -129,8 +145,8 @@
false),
RingerStream(AudioManager.STREAM_RING,
R.string.volume_icon_description_ringer,
- R.drawable.ic_audio_ring_notif,
- R.drawable.ic_audio_ring_notif_mute,
+ com.android.systemui.R.drawable.ic_ringer_audible,
+ com.android.systemui.R.drawable.ic_ringer_silent,
false),
VoiceStream(AudioManager.STREAM_VOICE_CALL,
R.string.volume_icon_description_incall,
@@ -149,8 +165,8 @@
true),
NotificationStream(AudioManager.STREAM_NOTIFICATION,
R.string.volume_icon_description_notification,
- R.drawable.ic_audio_notification,
- R.drawable.ic_audio_notification_mute,
+ com.android.systemui.R.drawable.ic_ringer_audible,
+ com.android.systemui.R.drawable.ic_ringer_silent,
true),
// for now, use media resources for master volume
MasterStream(STREAM_MASTER,
@@ -245,8 +261,11 @@
}
- public VolumePanel(Context context) {
+ public VolumePanel(Context context, ViewGroup parent, ZenModeController zenController) {
+ mTag = String.format("VolumePanel%s.%08x", parent == null ? "Dialog" : "", hashCode());
mContext = context;
+ mParent = parent;
+ mZenController = zenController;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
// For now, only show master volume if master volume is supported
@@ -258,74 +277,81 @@
streamRes.show = (streamRes.streamType == STREAM_MASTER);
}
}
-
- mDialog = new Dialog(context) {
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
- sConfirmSafeVolumeDialog == null) {
- forceTimeout();
- return true;
+ if (LOGD) Log.d(mTag, String.format("new VolumePanel hasParent=%s", parent != null));
+ final int layoutId = com.android.systemui.R.layout.volume_panel;
+ if (parent == null) {
+ // dialog mode
+ mDialog = new Dialog(context) {
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
+ sConfirmSafeVolumeDialog == null) {
+ forceTimeout();
+ return true;
+ }
+ return false;
}
- return false;
- }
- };
+ };
- // Change some window properties
- final Window window = mDialog.getWindow();
- final LayoutParams lp = window.getAttributes();
- lp.token = null;
- // Offset from the top
- lp.y = res.getDimensionPixelOffset(R.dimen.volume_panel_top);
- lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
- lp.windowAnimations = R.style.Animation_VolumePanel;
- window.setAttributes(lp);
- window.setGravity(Gravity.TOP);
- window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- window.requestFeature(Window.FEATURE_NO_TITLE);
- window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
- | LayoutParams.FLAG_NOT_TOUCH_MODAL
- | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+ // Change some window properties
+ final Window window = mDialog.getWindow();
+ final LayoutParams lp = window.getAttributes();
+ lp.token = null;
+ // Offset from the top
+ lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
+ lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.volume_panel_width);
+ lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
+ lp.windowAnimations = R.style.Animation_VolumePanel;
+ window.setBackgroundDrawableResource(com.android.systemui.R.drawable.qs_panel_background);
+ window.setAttributes(lp);
+ window.setGravity(Gravity.TOP);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ window.requestFeature(Window.FEATURE_NO_TITLE);
+ window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
+ | LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+ mDialog.setCanceledOnTouchOutside(true);
+ mDialog.setContentView(layoutId);
+ mDialog.setOnDismissListener(new OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mActiveStreamType = -1;
+ mAudioManager.forceVolumeControlStream(mActiveStreamType);
+ }
+ });
- mDialog.setCanceledOnTouchOutside(true);
- mDialog.setContentView(R.layout.volume_adjust);
- mDialog.setOnDismissListener(new OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- mActiveStreamType = -1;
- mAudioManager.forceVolumeControlStream(mActiveStreamType);
- }
- });
+ mDialog.create();
- mDialog.create();
+ mView = window.findViewById(R.id.content);
+ mView.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ resetTimeout();
+ return false;
+ }
+ });
- mView = window.findViewById(R.id.content);
- mView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- resetTimeout();
- return false;
- }
- });
-
- mPanel = (ViewGroup) mView.findViewById(R.id.visible_panel);
- mSliderGroup = (ViewGroup) mView.findViewById(R.id.slider_group);
- mMoreButton = mView.findViewById(R.id.expand_button);
- mDivider = mView.findViewById(R.id.expand_button_divider);
+ } else {
+ // embedded mode
+ mDialog = null;
+ mView = LayoutInflater.from(mContext).inflate(layoutId, parent, true);
+ }
+ mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
+ mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
+ mExpandButton = (ImageView) mView.findViewById(com.android.systemui.R.id.expand_button);
+ mExpandDivider = mView.findViewById(com.android.systemui.R.id.expand_button_divider);
+ mZenPanelStub = (ViewStub)mView.findViewById(com.android.systemui.R.id.zen_panel_stub);
+ mZenPanelDivider = mView.findViewById(com.android.systemui.R.id.zen_panel_divider);
mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
- // If we don't want to show multiple volumes, hide the settings button
- // and divider.
- mShowCombinedVolumes = !mVoiceCapable && !useMasterVolume;
- if (!mShowCombinedVolumes) {
- mMoreButton.setVisibility(View.GONE);
- mDivider.setVisibility(View.GONE);
- } else {
- mMoreButton.setOnClickListener(mClickListener);
- }
+ mZenModeCapable = !useMasterVolume && mZenController != null;
+ mZenPanelDivider.setVisibility(View.GONE);
+ mExpandButton.setOnClickListener(mClickListener);
+ updateZenMode(mZenController == null ? false : mZenController.isZen());
+ mZenController.addCallback(mZenCallback);
final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
@@ -334,7 +360,7 @@
listenToRingerMode();
}
- public void setLayoutDirection(int layoutDirection) {
+ private void setLayoutDirection(int layoutDirection) {
mPanel.setLayoutDirection(layoutDirection);
updateStates();
}
@@ -406,21 +432,19 @@
StreamResources streamRes = STREAMS[i];
final int streamType = streamRes.streamType;
- if (mVoiceCapable && streamRes == StreamResources.NotificationStream) {
- streamRes = StreamResources.RingerStream;
- }
final StreamControl sc = new StreamControl();
sc.streamType = streamType;
- sc.group = (ViewGroup) inflater.inflate(R.layout.volume_adjust_item, null);
+ sc.group = (ViewGroup) inflater.inflate(
+ com.android.systemui.R.layout.volume_panel_item, null);
sc.group.setTag(sc);
- sc.icon = (ImageView) sc.group.findViewById(R.id.stream_icon);
+ sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
sc.icon.setTag(sc);
sc.icon.setContentDescription(res.getString(streamRes.descRes));
sc.iconRes = streamRes.iconRes;
sc.iconMuteRes = streamRes.iconMuteRes;
sc.icon.setImageResource(sc.iconRes);
- sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar);
+ sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
@@ -431,34 +455,18 @@
}
private void reorderSliders(int activeStreamType) {
- mSliderGroup.removeAllViews();
+ mSliderPanel.removeAllViews();
final StreamControl active = mStreamControls.get(activeStreamType);
if (active == null) {
Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
mActiveStreamType = -1;
} else {
- mSliderGroup.addView(active.group);
+ mSliderPanel.addView(active.group);
mActiveStreamType = activeStreamType;
active.group.setVisibility(View.VISIBLE);
updateSlider(active);
- }
-
- addOtherVolumes();
- }
-
- private void addOtherVolumes() {
- if (!mShowCombinedVolumes) return;
-
- for (int i = 0; i < STREAMS.length; i++) {
- // Skip the phone specific ones and the active one
- final int streamType = STREAMS[i].streamType;
- if (!STREAMS[i].show || streamType == mActiveStreamType) {
- continue;
- }
- StreamControl sc = mStreamControls.get(streamType);
- mSliderGroup.addView(sc.group);
- updateSlider(sc);
+ updateZenMode(mZenController == null ? false : mZenController.isZen());
}
}
@@ -472,7 +480,7 @@
if (((sc.streamType == AudioManager.STREAM_RING) ||
(sc.streamType == AudioManager.STREAM_NOTIFICATION)) &&
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
- sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate);
+ sc.icon.setImageResource(com.android.systemui.R.drawable.ic_ringer_vibrate);
}
if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
// never disable touch interactions for remote playback, the muting is not tied to
@@ -486,32 +494,70 @@
}
}
+ public void setZenModePanelCallback(ZenModePanel.Callback callback) {
+ mZenPanelCallback = callback;
+ }
+
private void expand() {
- final int count = mSliderGroup.getChildCount();
- for (int i = 0; i < count; i++) {
- mSliderGroup.getChildAt(i).setVisibility(View.VISIBLE);
+ if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel);
+ if (mZenPanel == null) {
+ mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
+ mZenPanel.init(mZenController);
+ mZenPanel.setCallback(new ZenModePanel.Callback() {
+ @Override
+ public void onMoreSettings() {
+ if (mZenPanelCallback != null) {
+ mZenPanelCallback.onMoreSettings();
+ }
+ }
+
+ @Override
+ public void onInteraction() {
+ if (mZenPanelCallback != null) {
+ mZenPanelCallback.onInteraction();
+ }
+ }
+ });
}
- mMoreButton.setVisibility(View.INVISIBLE);
- mDivider.setVisibility(View.INVISIBLE);
+ mZenPanel.setVisibility(View.VISIBLE);
+ mZenPanelDivider.setVisibility(View.VISIBLE);
}
private void collapse() {
- mMoreButton.setVisibility(View.VISIBLE);
- mDivider.setVisibility(View.VISIBLE);
- final int count = mSliderGroup.getChildCount();
- for (int i = 1; i < count; i++) {
- mSliderGroup.getChildAt(i).setVisibility(View.GONE);
+ if (LOGD) Log.d(mTag, "collapse mZenPanel=" + mZenPanel);
+ if (mZenPanel != null) {
+ mZenPanel.setVisibility(View.GONE);
}
+ mZenPanelDivider.setVisibility(View.GONE);
}
public void updateStates() {
- final int count = mSliderGroup.getChildCount();
+ final int count = mSliderPanel.getChildCount();
for (int i = 0; i < count; i++) {
- StreamControl sc = (StreamControl) mSliderGroup.getChildAt(i).getTag();
+ StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
updateSlider(sc);
}
}
+ private void updateZenMode(boolean zen) {
+ if (mZenModeCapable) {
+ final boolean show = mActiveStreamType == AudioManager.STREAM_NOTIFICATION
+ || mActiveStreamType == AudioManager.STREAM_RING;
+ mExpandButton.setVisibility(show ? View.VISIBLE : View.GONE);
+ mExpandDivider.setVisibility(show ? View.VISIBLE : View.GONE);
+ mExpandButton.setImageResource(zen ? com.android.systemui.R.drawable.ic_vol_zen_on
+ : com.android.systemui.R.drawable.ic_vol_zen_off);
+ } else {
+ mExpandButton.setVisibility(View.GONE);
+ mExpandDivider.setVisibility(View.GONE);
+ }
+ }
+
+ public void postZenModeChanged(boolean zen) {
+ removeMessages(MSG_ZEN_MODE_CHANGED);
+ obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0).sendToTarget();
+ }
+
public void postVolumeChanged(int streamType, int flags) {
if (hasMessages(MSG_VOLUME_CHANGED)) return;
synchronized (this) {
@@ -582,8 +628,12 @@
}
public void postDismiss() {
- removeMessages(MSG_TIMEOUT);
- sendEmptyMessage(MSG_TIMEOUT);
+ forceTimeout();
+ }
+
+ public void postLayoutDirection(int layoutDirection) {
+ removeMessages(MSG_LAYOUT_DIRECTION);
+ obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection).sendToTarget();
}
/**
@@ -593,7 +643,7 @@
*/
protected void onVolumeChanged(int streamType, int flags) {
- if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
+ if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
synchronized (this) {
@@ -622,7 +672,7 @@
protected void onMuteChanged(int streamType, int flags) {
- if (LOGD) Log.d(TAG, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
+ if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
StreamControl sc = mStreamControls.get(streamType);
if (sc != null) {
@@ -638,7 +688,7 @@
mRingIsSilent = false;
if (LOGD) {
- Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType
+ Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
+ ", flags: " + flags + "), index: " + index);
}
@@ -707,7 +757,7 @@
}
case AudioService.STREAM_REMOTE_MUSIC: {
- if (LOGD) { Log.d(TAG, "showing remote volume "+index+" over "+ max); }
+ if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
break;
}
}
@@ -730,16 +780,18 @@
}
}
- if (!mDialog.isShowing()) {
+ if (!isShowing()) {
int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
// when the stream is for remote playback, use -1 to reset the stream type evaluation
mAudioManager.forceVolumeControlStream(stream);
// Showing dialog - use collapsed state
- if (mShowCombinedVolumes) {
+ if (mZenModeCapable) {
collapse();
}
- mDialog.show();
+ if (mDialog != null) {
+ mDialog.show();
+ }
}
// Do a little vibrate if applicable (only when going into vibrate mode)
@@ -751,6 +803,10 @@
}
}
+ private boolean isShowing() {
+ return mDialog != null ? mDialog.isShowing() : mParent.isAttachedToWindow();
+ }
+
protected void onPlaySound(int streamType, int flags) {
if (hasMessages(MSG_STOP_SOUNDS)) {
@@ -795,9 +851,9 @@
// streamType is the real stream type being affected, but for the UI sliders, we
// refer to AudioService.STREAM_REMOTE_MUSIC. We still play the beeps on the real
// stream type.
- if (LOGD) Log.d(TAG, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
+ if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
- if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || mDialog.isShowing()) {
+ if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
synchronized (this) {
if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
@@ -805,7 +861,7 @@
onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags);
}
} else {
- if (LOGD) Log.d(TAG, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
+ if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
}
if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
@@ -825,8 +881,8 @@
}
protected void onRemoteVolumeUpdateIfShown() {
- if (LOGD) Log.d(TAG, "onRemoteVolumeUpdateIfShown()");
- if (mDialog.isShowing()
+ if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
+ if (isShowing()
&& (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
&& (mStreamControls != null)) {
onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0);
@@ -842,7 +898,7 @@
* @param visible
*/
synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
- if (LOGD) Log.d(TAG, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
+ if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
boolean isVisible = (visible == 1);
for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
StreamResources streamRes = STREAMS[i];
@@ -857,7 +913,7 @@
}
protected void onDisplaySafeVolumeWarning(int flags) {
- if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || mDialog.isShowing()) {
+ if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || isShowing()) {
synchronized (sConfirmSafeVolumeLock) {
if (sConfirmSafeVolumeDialog != null) {
return;
@@ -907,7 +963,7 @@
mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
} catch (RuntimeException e) {
if (LOGD) {
- Log.d(TAG, "ToneGenerator constructor failed with "
+ Log.d(mTag, "ToneGenerator constructor failed with "
+ "RuntimeException: " + e);
}
}
@@ -976,8 +1032,10 @@
}
case MSG_TIMEOUT: {
- if (mDialog.isShowing()) {
- mDialog.dismiss();
+ if (isShowing()) {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ }
mActiveStreamType = -1;
}
synchronized (sConfirmSafeVolumeLock) {
@@ -988,7 +1046,7 @@
break;
}
case MSG_RINGER_MODE_CHANGED: {
- if (mDialog.isShowing()) {
+ if (isShowing()) {
updateStates();
}
break;
@@ -1010,17 +1068,30 @@
case MSG_DISPLAY_SAFE_VOLUME_WARNING:
onDisplaySafeVolumeWarning(msg.arg1);
break;
+
+ case MSG_LAYOUT_DIRECTION:
+ setLayoutDirection(msg.arg1);
+ break;
+
+ case MSG_ZEN_MODE_CHANGED:
+ updateZenMode(msg.arg1 != 0);
+ break;
}
}
- private void resetTimeout() {
+ public void resetTimeout() {
+ if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis());
removeMessages(MSG_TIMEOUT);
- sendMessageDelayed(obtainMessage(MSG_TIMEOUT), TIMEOUT_DELAY);
+ sendEmptyMessageDelayed(MSG_TIMEOUT, TIMEOUT_DELAY);
}
private void forceTimeout() {
removeMessages(MSG_TIMEOUT);
- sendMessage(obtainMessage(MSG_TIMEOUT));
+ sendEmptyMessage(MSG_TIMEOUT);
+ }
+
+ public ZenModeController getZenController() {
+ return mZenController;
}
private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
@@ -1061,10 +1132,22 @@
private final View.OnClickListener mClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
- if (v == mMoreButton) {
- expand();
+ if (v == mExpandButton && mZenController != null) {
+ final boolean newZen = !mZenController.isZen();
+ mZenController.setZen(newZen);
+ if (newZen) {
+ expand();
+ } else {
+ collapse();
+ }
}
resetTimeout();
}
};
+
+ private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
+ public void onZenChanged(boolean zen) {
+ updateZenMode(zen);
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 9bd75b7..7da90d8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -1,16 +1,22 @@
package com.android.systemui.volume;
import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.media.IVolumeController;
import android.net.Uri;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
+import com.android.systemui.R;
import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
/*
* Copyright (C) 2014 The Android Open Source Project
@@ -34,21 +40,21 @@
private static final Uri SETTING_URI = Settings.Global.getUriFor(SETTING);
private static final int DEFAULT = 1; // enabled by default
+ private final Handler mHandler = new Handler();
private AudioManager mAudioManager;
private VolumeController mVolumeController;
@Override
public void start() {
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ mVolumeController = new VolumeController(mContext);
+ putComponent(VolumeComponent.class, mVolumeController);
updateController();
mContext.getContentResolver().registerContentObserver(SETTING_URI, false, mObserver);
}
private void updateController() {
if (Settings.Global.getInt(mContext.getContentResolver(), SETTING, DEFAULT) != 0) {
- if (mVolumeController == null) {
- mVolumeController = new VolumeController(mContext);
- }
Log.d(TAG, "Registering volume controller");
mAudioManager.setVolumeController(mVolumeController);
} else {
@@ -57,7 +63,7 @@
}
}
- private final ContentObserver mObserver = new ContentObserver(new Handler()) {
+ private final ContentObserver mObserver = new ContentObserver(mHandler) {
public void onChange(boolean selfChange, Uri uri) {
if (SETTING_URI.equals(uri)) {
updateController();
@@ -66,13 +72,38 @@
};
/** For now, simply host an unmodified base volume panel in this process. */
- private final class VolumeController extends IVolumeController.Stub {
- private final VolumePanel mPanel;
+ private final class VolumeController extends IVolumeController.Stub implements VolumeComponent {
+ private final VolumePanel mDialogPanel;
+ private VolumePanel mPanel;
public VolumeController(Context context) {
- mPanel = new VolumePanel(context);
+ mPanel = new VolumePanel(context, null, new ZenModeControllerImpl(mContext, mHandler));
+ final int delay = context.getResources().getInteger(R.integer.feedback_start_delay);
+ mPanel.setZenModePanelCallback(new ZenModePanel.Callback() {
+ @Override
+ public void onMoreSettings() {
+ mHandler.removeCallbacks(mStartZenSettings);
+ mHandler.postDelayed(mStartZenSettings, delay);
+ }
+
+ @Override
+ public void onInteraction() {
+ mDialogPanel.resetTimeout();
+ }
+ });
+ mDialogPanel = mPanel;
}
+ private final Runnable mStartZenSettings = new Runnable() {
+ @Override
+ public void run() {
+ mDialogPanel.postDismiss();
+ final Intent intent = ZenModePanel.ZEN_SETTINGS;
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ }
+ };
+
@Override
public void hasNewRemotePlaybackInfo() throws RemoteException {
mPanel.postHasNewRemotePlaybackInfo();
@@ -114,12 +145,22 @@
@Override
public void setLayoutDirection(int layoutDirection)
throws RemoteException {
- mPanel.setLayoutDirection(layoutDirection);
+ mPanel.postLayoutDirection(layoutDirection);
}
@Override
public void dismiss() throws RemoteException {
mPanel.postDismiss();
}
+
+ @Override
+ public ZenModeController getZenController() {
+ return mDialogPanel.getZenController();
+ }
+
+ @Override
+ public void setVolumePanel(VolumePanel panel) {
+ mPanel = panel == null ? mDialogPanel : panel;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
new file mode 100644
index 0000000..77d267e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -0,0 +1,248 @@
+/*
+ * 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.volume;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings;
+import android.service.notification.Condition;
+import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+public class ZenModePanel extends LinearLayout {
+ private static final int[] MINUTES = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
+ public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
+
+ private final LayoutInflater mInflater;
+ private final HashSet<RadioButton> mRadioButtons = new HashSet<RadioButton>();
+ private final H mHandler = new H();
+ private LinearLayout mConditions;
+ private int mMinutesIndex = Arrays.binarySearch(MINUTES, 60); // default to one hour
+ private Callback mCallback;
+ private ZenModeController mController;
+ private boolean mRequestingConditions;
+
+ public ZenModePanel(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme));
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mConditions = (LinearLayout) findViewById(android.R.id.content);
+ findViewById(android.R.id.button2).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ fireMoreSettings();
+ }
+ });
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+ setRequestingConditions(visibility == VISIBLE);
+ }
+
+ /** Start or stop requesting relevant zen mode exit conditions */
+ private void setRequestingConditions(boolean requesting) {
+ if (mRequestingConditions == requesting) return;
+ mRequestingConditions = requesting;
+ if (mRequestingConditions) {
+ mController.addCallback(mZenCallback);
+ } else {
+ mController.removeCallback(mZenCallback);
+ }
+ mController.requestConditions(mRequestingConditions);
+ }
+
+ public void init(ZenModeController controller) {
+ mController = controller;
+ mConditions.removeAllViews();
+ bind(updateTimeCondition(), mConditions.getChildAt(0));
+ handleUpdateConditions(new Condition[0]);
+ }
+
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ private Condition updateTimeCondition() {
+ final int minutes = MINUTES[mMinutesIndex];
+ final long millis = System.currentTimeMillis() + minutes * 60 * 1000;
+ final Uri id = new Uri.Builder().scheme(Condition.SCHEME).authority("android")
+ .appendPath("countdown").appendPath(Long.toString(millis)).build();
+ final int num = minutes < 60 ? minutes : minutes / 60;
+ final int resId = minutes < 60
+ ? R.plurals.zen_mode_duration_minutes
+ : R.plurals.zen_mode_duration_hours;
+ final String caption = mContext.getResources().getQuantityString(resId, num, num);
+ return new Condition(id, caption, "", "", 0, Condition.STATE_TRUE,
+ Condition.FLAG_RELEVANT_NOW);
+ }
+
+ private void handleUpdateConditions(Condition[] conditions) {
+ final int newCount = conditions == null ? 0 : conditions.length;
+ for (int i = mConditions.getChildCount() - 1; i > newCount; i--) {
+ mConditions.removeViewAt(i);
+ }
+ for (int i = 0; i < newCount; i++) {
+ bind(conditions[i], mConditions.getChildAt(i + 1));
+ }
+ bind(null, mConditions.getChildAt(newCount + 1));
+ }
+
+ private void editTimeCondition(int delta) {
+ final int i = mMinutesIndex + delta;
+ if (i < 0 || i >= MINUTES.length) return;
+ mMinutesIndex = i;
+ final Condition c = updateTimeCondition();
+ bind(c, mConditions.getChildAt(0));
+ }
+
+ private void bind(final Condition condition, View convertView) {
+ final boolean enabled = condition == null || condition.state == Condition.STATE_TRUE;
+ final View row;
+ if (convertView == null) {
+ row = mInflater.inflate(R.layout.zen_mode_condition, this, false);
+ mConditions.addView(row);
+ } else {
+ row = convertView;
+ }
+ final int position = mConditions.indexOfChild(row);
+ final RadioButton rb = (RadioButton) row.findViewById(android.R.id.checkbox);
+ mRadioButtons.add(rb);
+ rb.setEnabled(enabled);
+ rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isChecked) {
+ for (RadioButton otherButton : mRadioButtons) {
+ if (otherButton == rb) continue;
+ otherButton.setChecked(false);
+ }
+ mController.select(condition);
+ fireInteraction();
+ }
+ }
+ });
+ final TextView title = (TextView) row.findViewById(android.R.id.title);
+ if (condition == null) {
+ title.setText(R.string.zen_mode_forever);
+ } else {
+ title.setText(condition.summary);
+ }
+ title.setEnabled(enabled);
+ title.setAlpha(enabled ? 1 : .5f);
+ final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
+ button1.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ rb.setChecked(true);
+ editTimeCondition(-1);
+ fireInteraction();
+ }
+ });
+
+ final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2);
+ button2.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ rb.setChecked(true);
+ editTimeCondition(1);
+ fireInteraction();
+ }
+ });
+ title.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ rb.setChecked(true);
+ fireInteraction();
+ }
+ });
+ if (position == 0) {
+ button1.setEnabled(mMinutesIndex > 0);
+ button2.setEnabled(mMinutesIndex < MINUTES.length - 1);
+ button1.setImageAlpha(button1.isEnabled() ? 0xff : 0x7f);
+ button2.setImageAlpha(button2.isEnabled() ? 0xff : 0x7f);
+ } else {
+ button1.setVisibility(View.GONE);
+ button2.setVisibility(View.GONE);
+ }
+ if (position == 0 && mConditions.getChildCount() == 1) {
+ rb.setChecked(true);
+ }
+ }
+
+ private void fireMoreSettings() {
+ if (mCallback != null) {
+ mCallback.onMoreSettings();
+ }
+ }
+
+ private void fireInteraction() {
+ if (mCallback != null) {
+ mCallback.onInteraction();
+ }
+ }
+
+ private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
+ @Override
+ public void onConditionsChanged(Condition[] conditions) {
+ mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
+ }
+ };
+
+ private final class H extends Handler {
+ private static final int UPDATE_CONDITIONS = 1;
+
+ private H() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == UPDATE_CONDITIONS) {
+ handleUpdateConditions((Condition[])msg.obj);
+ }
+ }
+ }
+
+ public interface Callback {
+ void onMoreSettings();
+ void onInteraction();
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 99771934..3c37902 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -66,6 +66,7 @@
import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
+import android.telephony.TelephonyManager;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -478,6 +479,7 @@
private static final int MSG_DISABLE_POINTER_LOCATION = 2;
private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
+ private static final int MSG_DISPATCH_SHOW_RECENTS = 5;
private class PolicyHandler extends Handler {
@Override
@@ -495,6 +497,9 @@
case MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK:
dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj);
break;
+ case MSG_DISPATCH_SHOW_RECENTS:
+ showRecentApps(false);
+ break;
}
}
}
@@ -1921,9 +1926,8 @@
ServiceManager.checkService(DreamService.DREAM_SERVICE));
}
- static ITelephony getTelephonyService() {
- return ITelephony.Stub.asInterface(
- ServiceManager.checkService(Context.TELEPHONY_SERVICE));
+ TelephonyManager getTelephonyService() {
+ return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
}
static IAudioService getAudioService() {
@@ -2006,14 +2010,10 @@
// If an incoming call is ringing, HOME is totally disabled.
// (The user is already on the InCallScreen at this point,
// and his ONLY options are to answer or reject the call.)
- try {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null && telephonyService.isRinging()) {
- Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
- return -1;
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
+ TelephonyManager telephonyManager = getTelephonyService();
+ if (telephonyManager != null && telephonyManager.isRinging()) {
+ Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
+ return -1;
}
// Delay handling home if a double-tap is possible.
@@ -2459,6 +2459,12 @@
}
}
+ @Override
+ public void showRecentApps() {
+ mHandler.removeMessages(MSG_DISPATCH_SHOW_RECENTS);
+ mHandler.sendEmptyMessage(MSG_DISPATCH_SHOW_RECENTS);
+ }
+
private void showRecentApps(boolean triggeredFromAltTab) {
mPreloadedRecentApps = false; // preloading no longer needs to be canceled
try {
@@ -3957,37 +3963,33 @@
}
}
if (down) {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null) {
- try {
- if (telephonyService.isRinging()) {
- // If an incoming call is ringing, either VOLUME key means
- // "silence ringer". We handle these keys here, rather than
- // in the InCallScreen, to make sure we'll respond to them
- // even if the InCallScreen hasn't come to the foreground yet.
- // Look for the DOWN event here, to agree with the "fallback"
- // behavior in the InCallScreen.
- Log.i(TAG, "interceptKeyBeforeQueueing:"
- + " VOLUME key-down while ringing: Silence ringer!");
+ TelephonyManager telephonyManager = getTelephonyService();
+ if (telephonyManager != null) {
+ if (telephonyManager.isRinging()) {
+ // If an incoming call is ringing, either VOLUME key means
+ // "silence ringer". We handle these keys here, rather than
+ // in the InCallScreen, to make sure we'll respond to them
+ // even if the InCallScreen hasn't come to the foreground yet.
+ // Look for the DOWN event here, to agree with the "fallback"
+ // behavior in the InCallScreen.
+ Log.i(TAG, "interceptKeyBeforeQueueing:"
+ + " VOLUME key-down while ringing: Silence ringer!");
- // Silence the ringer. (It's safe to call this
- // even if the ringer has already been silenced.)
- telephonyService.silenceRinger();
+ // Silence the ringer. (It's safe to call this
+ // even if the ringer has already been silenced.)
+ telephonyManager.silenceRinger();
- // And *don't* pass this key thru to the current activity
- // (which is probably the InCallScreen.)
- result &= ~ACTION_PASS_TO_USER;
- break;
- }
- if (telephonyService.isOffhook()
- && (result & ACTION_PASS_TO_USER) == 0) {
- // If we are in call but we decided not to pass the key to
- // the application, handle the volume change here.
- handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
- break;
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
+ // And *don't* pass this key thru to the current activity
+ // (which is probably the InCallScreen.)
+ result &= ~ACTION_PASS_TO_USER;
+ break;
+ }
+ if (telephonyManager.isOffhook()
+ && (result & ACTION_PASS_TO_USER) == 0) {
+ // If we are in call but we decided not to pass the key to
+ // the application, handle the volume change here.
+ handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
+ break;
}
}
@@ -4004,14 +4006,10 @@
case KeyEvent.KEYCODE_ENDCALL: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
- ITelephony telephonyService = getTelephonyService();
+ TelephonyManager telephonyManager = getTelephonyService();
boolean hungUp = false;
- if (telephonyService != null) {
- try {
- hungUp = telephonyService.endCall();
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
- }
+ if (telephonyManager != null) {
+ hungUp = telephonyManager.endCall();
}
interceptPowerKeyDown(!interactive || hungUp);
} else {
@@ -4047,23 +4045,19 @@
interceptScreenshotChord();
}
- ITelephony telephonyService = getTelephonyService();
+ TelephonyManager telephonyManager = getTelephonyService();
boolean hungUp = false;
- if (telephonyService != null) {
- try {
- if (telephonyService.isRinging()) {
- // Pressing Power while there's a ringing incoming
- // call should silence the ringer.
- telephonyService.silenceRinger();
- } else if ((mIncallPowerBehavior
- & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
- && telephonyService.isOffhook() && interactive) {
- // Otherwise, if "Power button ends call" is enabled,
- // the Power button will hang up any current active call.
- hungUp = telephonyService.endCall();
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
+ if (telephonyManager != null) {
+ if (telephonyManager.isRinging()) {
+ // Pressing Power while there's a ringing incoming
+ // call should silence the ringer.
+ telephonyManager.silenceRinger();
+ } else if ((mIncallPowerBehavior
+ & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
+ && telephonyManager.isOffhook() && interactive) {
+ // Otherwise, if "Power button ends call" is enabled,
+ // the Power button will hang up any current active call.
+ hungUp = telephonyManager.endCall();
}
}
interceptPowerKeyDown(!interactive || hungUp
@@ -4096,16 +4090,12 @@
case KeyEvent.KEYCODE_MEDIA_PAUSE:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
if (down) {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null) {
- try {
- if (!telephonyService.isIdle()) {
- // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
- // to avoid music playback.
- break;
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
+ TelephonyManager telephonyManager = getTelephonyService();
+ if (telephonyManager != null) {
+ if (!telephonyManager.isIdle()) {
+ // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
+ // to avoid music playback.
+ break;
}
}
}
@@ -4135,20 +4125,16 @@
case KeyEvent.KEYCODE_CALL: {
if (down) {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null) {
- try {
- if (telephonyService.isRinging()) {
- Log.i(TAG, "interceptKeyBeforeQueueing:"
- + " CALL key-down while ringing: Answer the call!");
- telephonyService.answerRingingCall();
+ TelephonyManager telephonyManager = getTelephonyService();
+ if (telephonyManager != null) {
+ if (telephonyManager.isRinging()) {
+ Log.i(TAG, "interceptKeyBeforeQueueing:"
+ + " CALL key-down while ringing: Answer the call!");
+ telephonyManager.answerRingingCall();
- // And *don't* pass this key thru to the current activity
- // (which is presumably the InCallScreen.)
- result &= ~ACTION_PASS_TO_USER;
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
+ // And *don't* pass this key thru to the current activity
+ // (which is presumably the InCallScreen.)
+ result &= ~ACTION_PASS_TO_USER;
}
}
}
@@ -5565,8 +5551,13 @@
pw.print(prefix); pw.print("mDemoHdmiRotation="); pw.print(mDemoHdmiRotation);
pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock);
pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation);
+
mStatusBarController.dump(pw, prefix);
mNavigationBarController.dump(pw, prefix);
PolicyControl.dump(prefix, pw);
+
+ if (mOrientationListener != null) {
+ mOrientationListener.dump(pw, prefix);
+ }
}
}
diff --git a/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java b/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java
index 0c77556..2cc33b5f 100644
--- a/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java
+++ b/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java
@@ -26,6 +26,9 @@
import android.util.FloatMath;
import android.util.Log;
import android.util.Slog;
+import android.util.TimeUtils;
+
+import java.io.PrintWriter;
/**
* A special helper class used by the WindowManager
@@ -181,6 +184,19 @@
*/
public abstract void onProposedRotationChanged(int rotation);
+ public void dump(PrintWriter pw, String prefix) {
+ synchronized (mLock) {
+ pw.println(prefix + TAG);
+ prefix += " ";
+ pw.println(prefix + "mEnabled=" + mEnabled);
+ pw.println(prefix + "mCurrentRotation=" + mCurrentRotation);
+ pw.println(prefix + "mSensor=" + mSensor);
+ pw.println(prefix + "mRate=" + mRate);
+
+ mSensorEventListener.dumpLocked(pw, prefix);
+ }
+ }
+
/**
* This class filters the raw accelerometer data and tries to detect actual changes in
* orientation. This is a very ill-defined problem so there are a lot of tweakable parameters,
@@ -342,6 +358,14 @@
/* ROTATION_270 */ { -25, 65 }
};
+ // The tilt angle below which we conclude that the user is holding the device
+ // overhead reading in bed and lock into that state.
+ private final int TILT_OVERHEAD_ENTER = -40;
+
+ // The tilt angle above which we conclude that the user would like a rotation
+ // change to occur and unlock from the overhead state.
+ private final int TILT_OVERHEAD_EXIT = -15;
+
// The gap angle in degrees between adjacent orientation angles for hysteresis.
// This creates a "dead zone" between the current orientation and a proposed
// adjacent orientation. No orientation proposal is made when the orientation
@@ -364,12 +388,18 @@
// Timestamp when the device last appeared to be flat for sure (the flat delay elapsed).
private long mFlatTimestampNanos;
+ private boolean mFlat;
// Timestamp when the device last appeared to be swinging.
private long mSwingTimestampNanos;
+ private boolean mSwinging;
// Timestamp when the device last appeared to be undergoing external acceleration.
private long mAccelerationTimestampNanos;
+ private boolean mAccelerating;
+
+ // Whether we are locked into an overhead usage mode.
+ private boolean mOverhead;
// History of observed tilt angles.
private static final int TILT_HISTORY_SIZE = 40;
@@ -381,6 +411,19 @@
return mProposedRotation;
}
+ public void dumpLocked(PrintWriter pw, String prefix) {
+ pw.println(prefix + "mProposedRotation=" + mProposedRotation);
+ pw.println(prefix + "mPredictedRotation=" + mPredictedRotation);
+ pw.println(prefix + "mLastFilteredX=" + mLastFilteredX);
+ pw.println(prefix + "mLastFilteredY=" + mLastFilteredY);
+ pw.println(prefix + "mLastFilteredZ=" + mLastFilteredZ);
+ pw.println(prefix + "mTiltHistory={last: " + getLastTiltLocked() + "}");
+ pw.println(prefix + "mFlat=" + mFlat);
+ pw.println(prefix + "mSwinging=" + mSwinging);
+ pw.println(prefix + "mAccelerating=" + mAccelerating);
+ pw.println(prefix + "mOverhead=" + mOverhead);
+ }
+
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@@ -478,7 +521,18 @@
// If the tilt angle is too close to horizontal then we cannot determine
// the orientation angle of the screen.
- if (Math.abs(tiltAngle) > MAX_TILT) {
+ if (tiltAngle <= TILT_OVERHEAD_ENTER) {
+ mOverhead = true;
+ } else if (tiltAngle >= TILT_OVERHEAD_EXIT) {
+ mOverhead = false;
+ }
+ if (mOverhead) {
+ if (LOG) {
+ Slog.v(TAG, "Ignoring sensor data, device is overhead: "
+ + "tiltAngle=" + tiltAngle);
+ }
+ clearPredictedRotationLocked();
+ } else if (Math.abs(tiltAngle) > MAX_TILT) {
if (LOG) {
Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
+ "tiltAngle=" + tiltAngle);
@@ -526,6 +580,9 @@
}
}
}
+ mFlat = isFlat;
+ mSwinging = isSwinging;
+ mAccelerating = isAccelerating;
// Determine new proposed rotation.
oldProposedRotation = mProposedRotation;
@@ -543,6 +600,7 @@
+ ", isAccelerating=" + isAccelerating
+ ", isFlat=" + isFlat
+ ", isSwinging=" + isSwinging
+ + ", isOverhead=" + mOverhead
+ ", timeUntilSettledMS=" + remainingMS(now,
mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS)
+ ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now,
@@ -660,8 +718,12 @@
mLastFilteredTimestampNanos = Long.MIN_VALUE;
mProposedRotation = -1;
mFlatTimestampNanos = Long.MIN_VALUE;
+ mFlat = false;
mSwingTimestampNanos = Long.MIN_VALUE;
+ mSwinging = false;
mAccelerationTimestampNanos = Long.MIN_VALUE;
+ mAccelerating = false;
+ mOverhead = false;
clearPredictedRotationLocked();
clearTiltHistoryLocked();
}
@@ -726,6 +788,11 @@
return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1;
}
+ private float getLastTiltLocked() {
+ int index = nextTiltHistoryIndexLocked(mTiltHistoryIndex);
+ return index >= 0 ? mTiltHistory[index] : Float.NaN;
+ }
+
private float remainingMS(long now, long until) {
return now >= until ? 0 : (until - now) * 0.000001f;
}
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 0d2cee8..5cfc49c 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -47,12 +47,14 @@
import android.util.Slog;
import com.android.internal.widget.ILockSettings;
+import com.android.internal.widget.ILockSettingsObserver;
import com.android.internal.widget.LockPatternUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -65,6 +67,9 @@
public class LockSettingsService extends ILockSettings.Stub {
private static final String PERMISSION = "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE";
+
+ private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
+
private final DatabaseHelper mOpenHelper;
private static final String TAG = "LockSettingsService";
@@ -85,6 +90,8 @@
private LockPatternUtils mLockPatternUtils;
private boolean mFirstCallToVold;
+ private final ArrayList<LockSettingsObserver> mObservers = new ArrayList<>();
+
public LockSettingsService(Context context) {
mContext = context;
// Open the database
@@ -222,6 +229,52 @@
return readFromDb(key, defaultValue, userId);
}
+ @Override
+ public void registerObserver(ILockSettingsObserver remote) throws RemoteException {
+ synchronized (mObservers) {
+ for (int i = 0; i < mObservers.size(); i++) {
+ if (mObservers.get(i).remote.asBinder() == remote.asBinder()) {
+ boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
+ if (isDebuggable) {
+ throw new IllegalStateException("Observer was already registered.");
+ } else {
+ Log.e(TAG, "Observer was already registered.");
+ return;
+ }
+ }
+ }
+ LockSettingsObserver o = new LockSettingsObserver();
+ o.remote = remote;
+ o.remote.asBinder().linkToDeath(o, 0);
+ mObservers.add(o);
+ }
+ }
+
+ @Override
+ public void unregisterObserver(ILockSettingsObserver remote) throws RemoteException {
+ synchronized (mObservers) {
+ for (int i = 0; i < mObservers.size(); i++) {
+ if (mObservers.get(i).remote.asBinder() == remote.asBinder()) {
+ mObservers.remove(i);
+ return;
+ }
+ }
+ }
+ }
+
+ public void notifyObservers(String key, int userId) {
+ synchronized (mObservers) {
+ for (int i = 0; i < mObservers.size(); i++) {
+ try {
+ mObservers.get(i).remote.onLockSettingChanged(key, userId);
+ } catch (RemoteException e) {
+ // The stack trace is not really helpful here.
+ Log.e(TAG, "Failed to notify ILockSettingsObserver: " + e);
+ }
+ }
+ }
+ }
+
private String getLockPatternFilename(int userId) {
String dataSystemDirectory =
android.os.Environment.getDataDirectory().getAbsolutePath() +
@@ -438,6 +491,7 @@
private void writeToDb(String key, String value, int userId) {
writeToDb(mOpenHelper.getWritableDatabase(), key, value, userId);
+ notifyObservers(key, userId);
}
private void writeToDb(SQLiteDatabase db, String key, String value, int userId) {
@@ -583,4 +637,13 @@
}
return null;
}
+
+ private class LockSettingsObserver implements DeathRecipient {
+ ILockSettingsObserver remote;
+
+ @Override
+ public void binderDied() {
+ mObservers.remove(this);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 248b44d..af082da 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -283,7 +283,7 @@
// before we decide it's never going to come up for real, when the process was
// started with a wrapper for instrumentation (such as Valgrind) because it
// could take much longer than usual.
- static final int PROC_START_TIMEOUT_WITH_WRAPPER = 300*1000;
+ static final int PROC_START_TIMEOUT_WITH_WRAPPER = 1200*1000;
// How long to wait after going idle before forcing apps to GC.
static final int GC_TIMEOUT = 5*1000;
@@ -409,7 +409,7 @@
/**
* List of intents that were used to start the most recent tasks.
*/
- final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
+ ArrayList<TaskRecord> mRecentTasks;
public class PendingAssistExtras extends Binder implements Runnable {
public final ActivityRecord activity;
@@ -822,6 +822,11 @@
final AppOpsService mAppOpsService;
/**
+ * Save recent tasks information across reboots.
+ */
+ final TaskPersister mTaskPersister;
+
+ /**
* Current configuration information. HistoryRecord objects are given
* a reference to this object to indicate which configuration they are
* currently running in, so this object must be kept immutable.
@@ -2138,6 +2143,7 @@
mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
mStackSupervisor = new ActivityStackSupervisor(this);
+ mTaskPersister = new TaskPersister(systemDir, mStackSupervisor);
mProcessCpuThread = new Thread("CpuTracker") {
@Override
@@ -7084,12 +7090,12 @@
private ActivityManager.RecentTaskInfo createRecentTaskInfoFromTaskRecord(TaskRecord tr) {
ActivityManager.RecentTaskInfo rti
= new ActivityManager.RecentTaskInfo();
- rti.id = tr.numActivities > 0 ? tr.taskId : -1;
+ rti.id = tr.mActivities.isEmpty() ? -1 : tr.taskId;
rti.persistentId = tr.taskId;
rti.baseIntent = new Intent(tr.getBaseIntent());
rti.origActivity = tr.origActivity;
rti.description = tr.lastDescription;
- rti.stackId = tr.stack.mStackId;
+ rti.stackId = tr.stack != null ? tr.stack.mStackId : -1;
rti.userId = tr.userId;
rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
return rti;
@@ -7323,6 +7329,9 @@
if (tr != null) {
tr.removeTaskActivitiesLocked(-1, false);
cleanUpRemovedTaskLocked(tr, flags);
+ if (tr.isPersistable) {
+ notifyTaskPersisterLocked(tr, true);
+ }
return true;
}
return false;
@@ -7562,14 +7571,11 @@
try {
synchronized (this) {
TaskRecord tr = recentTaskForIdLocked(taskId);
- if (tr != null) {
- return tr.stack.isHomeStack();
- }
+ return tr != null && tr.stack != null && tr.stack.isHomeStack();
}
} finally {
Binder.restoreCallingIdentity(ident);
}
- return false;
}
@Override
@@ -8638,6 +8644,10 @@
}
}
+ void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
+ mTaskPersister.notify(task, flush);
+ }
+
@Override
public boolean shutdown(int timeout) {
if (checkCallingPermission(android.Manifest.permission.SHUTDOWN)
@@ -8660,6 +8670,7 @@
synchronized (this) {
mProcessStats.shutdownLocked();
}
+ notifyTaskPersisterLocked(null, true);
return timedout;
}
@@ -9565,7 +9576,15 @@
if (goingCallback != null) goingCallback.run();
return;
}
-
+
+ if (mRecentTasks == null) {
+ mRecentTasks = mTaskPersister.restoreTasksLocked();
+ if (!mRecentTasks.isEmpty()) {
+ mStackSupervisor.createStackForRestoredTaskHistory(mRecentTasks);
+ }
+ mTaskPersister.startPersisting();
+ }
+
// Check to see if there are any update receivers to run.
if (!mDidUpdate) {
if (mWaitingUpdate) {
@@ -17182,7 +17201,7 @@
/**
* An implementation of IAppTask, that allows an app to manage its own tasks via
- * {@link android.app.ActivityManager#AppTask}. We keep track of the callingUid to ensure that
+ * {@link android.app.ActivityManager.AppTask}. We keep track of the callingUid to ensure that
* only the process that calls getAppTasks() can call the AppTask methods.
*/
class AppTaskImpl extends IAppTask.Stub {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index dbe2ca1..b429b93 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -16,14 +16,15 @@
package com.android.server.am;
+import android.app.ActivityManager.TaskDescription;
import android.os.PersistableBundle;
import android.os.Trace;
import com.android.internal.app.ResolverActivity;
+import com.android.internal.util.XmlUtils;
import com.android.server.AttributeCache;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
-import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ResultInfo;
import android.content.ComponentName;
@@ -48,7 +49,11 @@
import android.util.TimeUtils;
import android.view.IApplicationToken;
import android.view.WindowManager;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -62,6 +67,19 @@
static final boolean DEBUG_SAVED_STATE = ActivityStackSupervisor.DEBUG_SAVED_STATE;
final public static String RECENTS_PACKAGE_NAME = "com.android.systemui.recent";
+ private static final String TAG_ACTIVITY = "activity";
+ private static final String ATTR_ID = "id";
+ private static final String TAG_INTENT = "intent";
+ private static final String ATTR_USERID = "user_id";
+ private static final String TAG_PERSISTABLEBUNDLE = "persistable_bundle";
+ private static final String ATTR_LAUNCHEDFROMUID = "launched_from_uid";
+ private static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package";
+ private static final String ATTR_RESOLVEDTYPE = "resolved_type";
+ private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
+ private static final String ATTR_TASKDESCRIPTIONLABEL = "task_description_label";
+ private static final String ATTR_TASKDESCRIPTIONCOLOR = "task_description_color";
+ private static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
+
final ActivityManagerService service; // owner
final IApplicationToken.Stub appToken; // window manager token
final ActivityInfo info; // all about me
@@ -97,6 +115,7 @@
int windowFlags; // custom window flags for preview window.
TaskRecord task; // the task this is in.
ThumbnailHolder thumbHolder; // where our thumbnails should go.
+ long createTime = System.currentTimeMillis();
long displayStartTime; // when we started launching this activity
long fullyDrawnStartTime; // when we started launching this activity
long startTime; // last time this activity was started
@@ -149,7 +168,7 @@
boolean mStartingWindowShown = false;
ActivityContainer mInitialActivityContainer;
- ActivityManager.TaskDescription taskDescription; // the recents information for this activity
+ TaskDescription taskDescription; // the recents information for this activity
void dump(PrintWriter pw, String prefix) {
final long now = SystemClock.uptimeMillis();
@@ -490,14 +509,6 @@
(newTask == null ? null : newTask.stack));
}
}
- if (inHistory && !finishing) {
- if (task != null) {
- task.numActivities--;
- }
- if (newTask != null) {
- newTask.numActivities++;
- }
- }
if (newThumbHolder == null) {
newThumbHolder = newTask;
}
@@ -527,9 +538,6 @@
void putInHistory() {
if (!inHistory) {
inHistory = true;
- if (task != null && !finishing) {
- task.numActivities++;
- }
}
}
@@ -537,7 +545,6 @@
if (inHistory) {
inHistory = false;
if (task != null && !finishing) {
- task.numActivities--;
task = null;
}
clearOptionsLocked();
@@ -560,12 +567,13 @@
return mActivityType == APPLICATION_ACTIVITY_TYPE;
}
+ boolean isPersistable() {
+ return (info.flags & ActivityInfo.FLAG_PERSISTABLE) != 0;
+ }
+
void makeFinishing() {
if (!finishing) {
finishing = true;
- if (task != null && inHistory) {
- task.numActivities--;
- }
if (stopped) {
clearOptionsLocked();
}
@@ -767,6 +775,9 @@
"Setting thumbnail of " + this + " holder " + thumbHolder
+ " to " + newThumbnail);
thumbHolder.lastThumbnail = newThumbnail;
+ if (isPersistable()) {
+ mStackSupervisor.mService.notifyTaskPersisterLocked(task, false);
+ }
}
thumbHolder.lastDescription = description;
}
@@ -1042,7 +1053,134 @@
return null;
}
- private String activityTypeToString(int type) {
+ void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+ out.attribute(null, ATTR_ID, String.valueOf(createTime));
+ out.attribute(null, ATTR_LAUNCHEDFROMUID, String.valueOf(launchedFromUid));
+ if (launchedFromPackage != null) {
+ out.attribute(null, ATTR_LAUNCHEDFROMPACKAGE, launchedFromPackage);
+ }
+ if (resolvedType != null) {
+ out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType);
+ }
+ out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified));
+ out.attribute(null, ATTR_USERID, String.valueOf(userId));
+ if (taskDescription != null) {
+ final String label = taskDescription.getLabel();
+ if (label != null) {
+ out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, label);
+ }
+ final int colorPrimary = taskDescription.getPrimaryColor();
+ if (colorPrimary != 0) {
+ out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR, Integer.toHexString(colorPrimary));
+ }
+ final Bitmap icon = taskDescription.getIcon();
+ if (icon != null) {
+ TaskPersister.saveImage(icon, String.valueOf(task.taskId) + ACTIVITY_ICON_SUFFIX +
+ createTime);
+ }
+ }
+
+ out.startTag(null, TAG_INTENT);
+ intent.saveToXml(out);
+ out.endTag(null, TAG_INTENT);
+
+ if (isPersistable() && persistentState != null) {
+ out.startTag(null, TAG_PERSISTABLEBUNDLE);
+ persistentState.saveToXml(out);
+ out.endTag(null, TAG_PERSISTABLEBUNDLE);
+ }
+ }
+
+ static ActivityRecord restoreFromXml(XmlPullParser in, int taskId,
+ ActivityStackSupervisor stackSupervisor) throws IOException, XmlPullParserException {
+ Intent intent = null;
+ PersistableBundle persistentState = null;
+ int launchedFromUid = 0;
+ String launchedFromPackage = null;
+ String resolvedType = null;
+ boolean componentSpecified = false;
+ int userId = 0;
+ String activityLabel = null;
+ int activityColor = 0;
+ long createTime = -1;
+ final int outerDepth = in.getDepth();
+
+ for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
+ final String attrName = in.getAttributeName(attrNdx);
+ final String attrValue = in.getAttributeValue(attrNdx);
+ if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "ActivityRecord: attribute name=" +
+ attrName + " value=" + attrValue);
+ if (ATTR_ID.equals(attrName)) {
+ createTime = Long.valueOf(attrValue);
+ } else if (ATTR_LAUNCHEDFROMUID.equals(attrName)) {
+ launchedFromUid = Integer.valueOf(attrValue);
+ } else if (ATTR_LAUNCHEDFROMPACKAGE.equals(attrName)) {
+ launchedFromPackage = attrValue;
+ } else if (ATTR_RESOLVEDTYPE.equals(attrName)) {
+ resolvedType = attrValue;
+ } else if (ATTR_COMPONENTSPECIFIED.equals(attrName)) {
+ componentSpecified = Boolean.valueOf(attrValue);
+ } else if (ATTR_USERID.equals(attrName)) {
+ userId = Integer.valueOf(attrValue);
+ } else if (ATTR_TASKDESCRIPTIONLABEL.equals(attrName)) {
+ activityLabel = attrValue;
+ } else if (ATTR_TASKDESCRIPTIONCOLOR.equals(attrName)) {
+ activityColor = (int) Long.parseLong(attrValue, 16);
+ } else {
+ Log.d(TAG, "Unknown ActivityRecord attribute=" + attrName);
+ }
+ }
+
+ int event;
+ while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+ (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
+ if (event == XmlPullParser.START_TAG) {
+ final String name = in.getName();
+ if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
+ "ActivityRecord: START_TAG name=" + name);
+ if (TAG_INTENT.equals(name)) {
+ intent = Intent.restoreFromXml(in);
+ if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
+ "ActivityRecord: intent=" + intent);
+ } else if (TAG_PERSISTABLEBUNDLE.equals(name)) {
+ persistentState = PersistableBundle.restoreFromXml(in);
+ if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
+ "ActivityRecord: persistentState=" + persistentState);
+ } else {
+ Slog.w(TAG, "restoreActivity: unexpected name=" + name);
+ XmlUtils.skipCurrentTag(in);
+ }
+ }
+ }
+
+ if (intent == null) {
+ throw new XmlPullParserException("restoreActivity error intent=" + intent);
+ }
+
+ final ActivityManagerService service = stackSupervisor.mService;
+ final ActivityInfo aInfo = stackSupervisor.resolveActivity(intent, resolvedType, 0, null,
+ null, userId);
+ if (aInfo == null) {
+ throw new XmlPullParserException("restoreActivity resolver error.");
+ }
+ final ActivityRecord r = new ActivityRecord(service, /*caller*/null, launchedFromUid,
+ launchedFromPackage, intent, resolvedType, aInfo, service.getConfiguration(),
+ null, null, 0, componentSpecified, stackSupervisor, null, null);
+
+ r.persistentState = persistentState;
+
+ Bitmap icon = null;
+ if (createTime >= 0) {
+ icon = TaskPersister.restoreImage(String.valueOf(taskId) + ACTIVITY_ICON_SUFFIX +
+ createTime);
+ }
+ r.taskDescription = new TaskDescription(activityLabel, icon, activityColor);
+ r.createTime = createTime;
+
+ return r;
+ }
+
+ private static String activityTypeToString(int type) {
switch (type) {
case APPLICATION_ACTIVITY_TYPE: return "APPLICATION_ACTIVITY_TYPE";
case HOME_ACTIVITY_TYPE: return "HOME_ACTIVITY_TYPE";
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 33e59a7..a0440cb 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -30,9 +30,14 @@
import static com.android.server.am.ActivityManagerService.DEBUG_VISBILITY;
import static com.android.server.am.ActivityManagerService.VALIDATE_TOKENS;
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
+
import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_APP;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE;
+import static com.android.server.am.ActivityStackSupervisor.DEBUG_SCREENSHOTS;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
@@ -342,6 +347,10 @@
mWindowManager = mService.mWindowManager;
mStackId = activityContainer.mStackId;
mCurrentUser = mService.mCurrentUserId;
+ // Get the activity screenshot thumbnail dimensions
+ Resources res = mService.mContext.getResources();
+ mThumbnailWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+ mThumbnailHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
}
/**
@@ -725,42 +734,54 @@
}
}
+ /**
+ * This resets the saved state from the last screenshot, forcing a new screenshot to be taken
+ * again when requested.
+ */
+ private void invalidateLastScreenshot() {
+ mLastScreenshotActivity = null;
+ if (mLastScreenshotBitmap != null) {
+ mLastScreenshotBitmap.recycle();
+ }
+ mLastScreenshotBitmap = null;
+ }
+
public final Bitmap screenshotActivities(ActivityRecord who) {
+ if (DEBUG_SCREENSHOTS) Slog.d(TAG, "screenshotActivities: " + who);
if (who.noDisplay) {
+ if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tNo display");
return null;
}
TaskRecord tr = who.task;
- if (mService.getMostRecentTask() != tr && tr.intent != null &&
- (tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
- // If this task is being excluded from recents, we don't want to take
- // the expense of capturing a thumbnail, since we will never show it.
+ if (mService.getMostRecentTask() != tr || isHomeStack()) {
+ // This is an optimization -- since we never show Home or Recents within Recents itself,
+ // we can just go ahead and skip taking the screenshot if this is the home stack. In
+ // the case where the most recent task is not the task that was supplied, then the stack
+ // has changed, so invalidate the last screenshot().
+ invalidateLastScreenshot();
+ if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tIs Home stack? " + isHomeStack());
return null;
}
- Resources res = mService.mContext.getResources();
int w = mThumbnailWidth;
int h = mThumbnailHeight;
- if (w < 0) {
- mThumbnailWidth = w =
- res.getDimensionPixelSize(com.android.internal.R.dimen.recents_thumbnail_width);
- mThumbnailHeight = h =
- res.getDimensionPixelSize(com.android.internal.R.dimen.recents_thumbnail_height);
- }
-
if (w > 0) {
if (who != mLastScreenshotActivity || mLastScreenshotBitmap == null
|| mLastScreenshotActivity.state == ActivityState.RESUMED
|| mLastScreenshotBitmap.getWidth() != w
|| mLastScreenshotBitmap.getHeight() != h) {
+ if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tUpdating screenshot");
mLastScreenshotActivity = who;
mLastScreenshotBitmap = mWindowManager.screenshotApplications(
who.appToken, Display.DEFAULT_DISPLAY, w, h, SCREENSHOT_FORCE_565);
}
if (mLastScreenshotBitmap != null) {
+ if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tReusing last screenshot");
return mLastScreenshotBitmap.copy(mLastScreenshotBitmap.getConfig(), true);
}
}
+ Slog.e(TAG, "Invalid thumbnail dimensions: " + w + "x" + h);
return null;
}
@@ -863,7 +884,10 @@
final ActivityRecord r = isInStackLocked(token);
if (r != null) {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
- r.persistentState = persistentState;
+ if (persistentState != null) {
+ r.persistentState = persistentState;
+ mService.notifyTaskPersisterLocked(r.task, false);
+ }
if (mPausingActivity == r) {
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
+ (timeout ? " (due to timeout)" : " (pause complete)"));
@@ -885,7 +909,10 @@
mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
return;
}
- r.persistentState = persistentState;
+ if (persistentState != null) {
+ r.persistentState = persistentState;
+ mService.notifyTaskPersisterLocked(r.task, false);
+ }
if (DEBUG_SAVED_STATE) Slog.i(TAG, "Saving icicle of " + r + ": " + icicle);
if (icicle != null) {
// If icicle is null, this is happening due to a timeout, so we
@@ -1032,40 +1059,12 @@
} else {
next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
}
- }
- /**
- * Determine if home should be visible below the passed record.
- * @param record activity we are querying for.
- * @return true if home is visible below the passed activity, false otherwise.
- */
- boolean isActivityOverHome(ActivityRecord record) {
- // Start at record and go down, look for either home or a visible fullscreen activity.
- final TaskRecord recordTask = record.task;
- for (int taskNdx = mTaskHistory.indexOf(recordTask); taskNdx >= 0; --taskNdx) {
- TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- final int startNdx =
- task == recordTask ? activities.indexOf(record) : activities.size() - 1;
- for (int activityNdx = startNdx; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
- if (r.isHomeActivity()) {
- return true;
- }
- if (!r.finishing && r.fullscreen) {
- // Passed activity is over a fullscreen activity.
- return false;
- }
- }
- if (task.mOnTopOfHome) {
- // Got to the bottom of a task on top of home without finding a visible fullscreen
- // activity. Home is visible.
- return true;
- }
+ // If we are resuming the activity that we had last screenshotted, then we know it will be
+ // updated, so invalidate the last screenshot to ensure we take a fresh one when requested
+ if (next == mLastScreenshotActivity) {
+ invalidateLastScreenshot();
}
- // Got to the bottom of this stack and still don't know. If this is over the home stack
- // then record is over home. May not work if we ever get more than two layers.
- return mStackSupervisor.isFrontStack(this);
}
private void setVisibile(ActivityRecord r, boolean visible) {
@@ -1097,7 +1096,8 @@
for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) {
final ArrayList<TaskRecord> tasks = mStacks.get(i).getAllTasks();
for (int taskNdx = 0; taskNdx < tasks.size(); taskNdx++) {
- final ArrayList<ActivityRecord> activities = tasks.get(taskNdx).mActivities;
+ final TaskRecord task = tasks.get(taskNdx);
+ final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = 0; activityNdx < activities.size(); activityNdx++) {
final ActivityRecord r = activities.get(activityNdx);
@@ -1108,7 +1108,7 @@
// - Full Screen Activity OR
// - On top of Home and our stack is NOT home
if (!r.finishing && r.visible && (r.fullscreen ||
- (!isHomeStack() && r.frontOfTask && tasks.get(taskNdx).mOnTopOfHome))) {
+ (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()))) {
return false;
}
}
@@ -1236,7 +1236,7 @@
// At this point, nothing else needs to be shown
if (DEBUG_VISBILITY) Slog.v(TAG, "Fullscreen: at " + r);
behindFullscreen = true;
- } else if (!isHomeStack() && r.frontOfTask && task.mOnTopOfHome) {
+ } else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r);
behindFullscreen = true;
}
@@ -1390,6 +1390,7 @@
final boolean userLeaving = mStackSupervisor.mUserLeaving;
mStackSupervisor.mUserLeaving = false;
+ final TaskRecord prevTask = prev != null ? prev.task : null;
if (next == null) {
// There are no more activities! Let's just start up the
// Launcher...
@@ -1397,7 +1398,10 @@
if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: No more activities go home");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
// Only resume home if on home display
- return isOnHomeDisplay() && mStackSupervisor.resumeHomeActivity(prev);
+ final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
+ HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
+ return isOnHomeDisplay() &&
+ mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
}
next.delayedResume = false;
@@ -1416,22 +1420,24 @@
}
final TaskRecord nextTask = next.task;
- final TaskRecord prevTask = prev != null ? prev.task : null;
if (prevTask != null && prevTask.stack == this &&
- prevTask.mOnTopOfHome && prev.finishing && prev.frontOfTask) {
+ prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) {
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
if (prevTask == nextTask) {
prevTask.setFrontOfTask();
} else if (prevTask != topTask()) {
- // This task is going away but it was supposed to return to the home task.
+ // This task is going away but it was supposed to return to the home stack.
// Now the task above it has to return to the home task instead.
final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
- mTaskHistory.get(taskNdx).mOnTopOfHome = true;
+ mTaskHistory.get(taskNdx).setTaskToReturnTo(HOME_ACTIVITY_TYPE);
} else {
if (DEBUG_STATES && isOnHomeDisplay()) Slog.d(TAG,
"resumeTopActivityLocked: Launching home next");
// Only resume home if on home display
- return isOnHomeDisplay() && mStackSupervisor.resumeHomeActivity(prev);
+ final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
+ HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
+ return isOnHomeDisplay() &&
+ mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
}
}
@@ -1802,10 +1808,11 @@
ActivityStack lastStack = mStackSupervisor.getLastStack();
final boolean fromHome = lastStack.isHomeStack();
if (!isHomeStack() && (fromHome || topTask() != task)) {
- task.mOnTopOfHome = fromHome;
+ task.setTaskToReturnTo(fromHome ?
+ lastStack.topTask().taskType : APPLICATION_ACTIVITY_TYPE);
}
} else {
- task.mOnTopOfHome = false;
+ task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
}
mTaskHistory.remove(task);
@@ -1821,6 +1828,7 @@
++stackNdx;
}
mTaskHistory.add(stackNdx, task);
+ updateTaskMovement(task, true);
}
final void startActivityLocked(ActivityRecord r, boolean newTask,
@@ -2349,8 +2357,8 @@
ActivityRecord next = topRunningActivityLocked(null);
if (next != r) {
final TaskRecord task = r.task;
- if (r.frontOfTask && task == topTask() && task.mOnTopOfHome) {
- mStackSupervisor.moveHomeToTop();
+ if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) {
+ mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo());
}
}
ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
@@ -2834,8 +2842,9 @@
if (task != null && task.removeActivity(r)) {
if (DEBUG_STACK) Slog.i(TAG,
"removeActivityFromHistoryLocked: last activity removed from " + this);
- if (mStackSupervisor.isFrontStack(this) && task == topTask() && task.mOnTopOfHome) {
- mStackSupervisor.moveHomeToTop();
+ if (mStackSupervisor.isFrontStack(this) && task == topTask() &&
+ task.isOverHomeStack()) {
+ mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo());
}
removeTask(task);
}
@@ -3138,14 +3147,28 @@
mWindowManager.prepareAppTransition(transit, false);
}
- void moveHomeTaskToTop() {
+ void updateTaskMovement(TaskRecord task, boolean toFront) {
+ if (task.isPersistable) {
+ task.mLastTimeMoved = System.currentTimeMillis();
+ // Sign is used to keep tasks sorted when persisted. Tasks sent to the bottom most
+ // recently will be most negative, tasks sent to the bottom before that will be less
+ // negative. Similarly for recent tasks moved to the top which will be most positive.
+ if (!toFront) {
+ task.mLastTimeMoved *= -1;
+ }
+ }
+ }
+
+ void moveHomeStackTaskToTop(int homeStackTaskType) {
final int top = mTaskHistory.size() - 1;
for (int taskNdx = top; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- if (task.isHomeTask()) {
- if (DEBUG_TASKS || DEBUG_STACK) Slog.d(TAG, "moveHomeTaskToTop: moving " + task);
+ if (task.taskType == homeStackTaskType) {
+ if (DEBUG_TASKS || DEBUG_STACK)
+ Slog.d(TAG, "moveHomeStackTaskToTop: moving " + task);
mTaskHistory.remove(taskNdx);
mTaskHistory.add(top, task);
+ updateTaskMovement(task, true);
mWindowManager.moveTaskToTop(task.taskId);
return;
}
@@ -3247,19 +3270,19 @@
mTaskHistory.remove(tr);
mTaskHistory.add(0, tr);
+ updateTaskMovement(tr, false);
// There is an assumption that moving a task to the back moves it behind the home activity.
// We make sure here that some activity in the stack will launch home.
- ActivityRecord lastActivity = null;
int numTasks = mTaskHistory.size();
for (int taskNdx = numTasks - 1; taskNdx >= 1; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- if (task.mOnTopOfHome) {
+ if (task.isOverHomeStack()) {
break;
}
if (taskNdx == 1) {
// Set the last task before tr to go to home.
- task.mOnTopOfHome = true;
+ task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
}
}
@@ -3280,9 +3303,10 @@
}
final TaskRecord task = mResumedActivity != null ? mResumedActivity.task : null;
- if (task == tr && tr.mOnTopOfHome || numTasks <= 1 && isOnHomeDisplay()) {
- tr.mOnTopOfHome = false;
- return mStackSupervisor.resumeHomeActivity(null);
+ if (task == tr && tr.isOverHomeStack() || numTasks <= 1 && isOnHomeDisplay()) {
+ final int taskToReturnTo = tr.getTaskToReturnTo();
+ tr.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+ return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null);
}
mStackSupervisor.resumeTopActivitiesLocked();
@@ -3723,10 +3747,14 @@
final int taskNdx = mTaskHistory.indexOf(task);
final int topTaskNdx = mTaskHistory.size() - 1;
- if (task.mOnTopOfHome && taskNdx < topTaskNdx) {
- mTaskHistory.get(taskNdx + 1).mOnTopOfHome = true;
+ if (task.isOverHomeStack() && taskNdx < topTaskNdx) {
+ final TaskRecord nextTask = mTaskHistory.get(taskNdx + 1);
+ if (!nextTask.isOverHomeStack()) {
+ nextTask.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+ }
}
mTaskHistory.remove(task);
+ updateTaskMovement(task, true);
if (task.mActivities.isEmpty()) {
final boolean isVoiceSession = task.voiceSession != null;
@@ -3758,7 +3786,8 @@
TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
boolean toTop) {
- TaskRecord task = new TaskRecord(taskId, info, intent, voiceSession, voiceInteractor);
+ TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
+ voiceInteractor);
addTask(task, toTop, false);
return task;
}
@@ -3773,6 +3802,7 @@
insertTaskAtTop(task);
} else {
mTaskHistory.add(0, task);
+ updateTaskMovement(task, false);
}
if (!moving && task.voiceSession != null) {
try {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 252c0bb..92f41f7 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -31,6 +31,9 @@
import static com.android.server.am.ActivityManagerService.DEBUG_USER_LEAVING;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
import static com.android.server.am.ActivityManagerService.TAG;
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import android.app.Activity;
import android.app.ActivityManager;
@@ -45,6 +48,7 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.IActivityManager.WaitResult;
import android.app.ResultInfo;
+import android.app.StatusBarManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentSender;
@@ -73,6 +77,7 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
@@ -88,11 +93,13 @@
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.TransferPipe;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.wm.WindowManagerService;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -106,6 +113,7 @@
static final boolean DEBUG_SAVED_STATE = DEBUG || false;
static final boolean DEBUG_STATES = DEBUG || false;
static final boolean DEBUG_IDLE = DEBUG || false;
+ static final boolean DEBUG_SCREENSHOTS = DEBUG || false;
public static final int HOME_STACK_ID = 0;
@@ -127,9 +135,15 @@
static final int HANDLE_DISPLAY_CHANGED = FIRST_SUPERVISOR_STACK_MSG + 6;
static final int HANDLE_DISPLAY_REMOVED = FIRST_SUPERVISOR_STACK_MSG + 7;
static final int CONTAINER_CALLBACK_VISIBILITY = FIRST_SUPERVISOR_STACK_MSG + 8;
+ static final int LOCK_TASK_START_MSG = FIRST_SUPERVISOR_STACK_MSG + 9;
+ static final int LOCK_TASK_END_MSG = FIRST_SUPERVISOR_STACK_MSG + 10;
private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
+ /** Status Bar Service **/
+ private IBinder mToken = new Binder();
+ private IStatusBarService mStatusBarService;
+
// For debugging to make sure the caller when acquiring/releasing our
// wake lock is the system process.
static final boolean VALIDATE_WAKE_LOCK_CALLER = false;
@@ -248,6 +262,21 @@
mLaunchingActivity.setReferenceCounted(false);
}
+ // This function returns a IStatusBarService. The value is from ServiceManager.
+ // getService and is cached.
+ private IStatusBarService getStatusBarService() {
+ synchronized (mService) {
+ if (mStatusBarService == null) {
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
+ if (mStatusBarService == null) {
+ Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
+ }
+ }
+ return mStatusBarService;
+ }
+ }
+
void setWindowManager(WindowManagerService wm) {
synchronized (mService) {
mWindowManager = wm;
@@ -318,18 +347,27 @@
}
}
- void moveHomeToTop() {
+ void moveHomeStackTaskToTop(int homeStackTaskType) {
+ if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
+ mWindowManager.showRecentApps();
+ return;
+ }
moveHomeStack(true);
- mHomeStack.moveHomeTaskToTop();
+ mHomeStack.moveHomeStackTaskToTop(homeStackTaskType);
}
- boolean resumeHomeActivity(ActivityRecord prev) {
- moveHomeToTop();
- if (prev != null) {
- prev.task.mOnTopOfHome = false;
+ boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev) {
+ if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
+ mWindowManager.showRecentApps();
+ return false;
}
+ moveHomeStackTaskToTop(homeStackTaskType);
+ if (prev != null) {
+ prev.task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+ }
+
ActivityRecord r = mHomeStack.topRunningActivityLocked(null);
- if (r != null && r.isHomeActivity()) {
+ if (r != null && (r.isHomeActivity() || r.isRecentsActivity())) {
mService.setFocusedActivityLocked(r);
return resumeTopActivitiesLocked(mHomeStack, prev, null);
}
@@ -370,6 +408,12 @@
return null;
}
+ void setNextTaskId(int taskId) {
+ if (taskId > mCurTaskId) {
+ mCurTaskId = taskId;
+ }
+ }
+
int getNextTaskId() {
do {
mCurTaskId++;
@@ -677,7 +721,7 @@
}
void startHomeActivity(Intent intent, ActivityInfo aInfo) {
- moveHomeToTop();
+ moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE);
startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null, 0,
null, false, null, null);
}
@@ -1194,8 +1238,7 @@
requestCode = sourceRecord.requestCode;
sourceRecord.resultTo = null;
if (resultRecord != null) {
- resultRecord.removeResultsLocked(
- sourceRecord, resultWho, requestCode);
+ resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
}
if (sourceRecord.launchedFromUid == callingUid) {
// The new activity is being launched from the same uid as the previous
@@ -1367,7 +1410,7 @@
return err;
}
- ActivityStack adjustStackFocus(ActivityRecord r) {
+ ActivityStack adjustStackFocus(ActivityRecord r, boolean newTask) {
final TaskRecord task = r.task;
if (r.isApplicationActivity() || (task != null && task.isApplicationTask())) {
if (task != null) {
@@ -1392,7 +1435,8 @@
return container.mStack;
}
- if (mFocusedStack != mHomeStack) {
+ if (mFocusedStack != mHomeStack && (!newTask ||
+ mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
"adjustStackFocus: Have a focused stack=" + mFocusedStack);
return mFocusedStack;
@@ -1445,7 +1489,7 @@
// We'll invoke onUserLeaving before onPause only if the launching
// activity did not explicitly state that this is an automated launch.
- mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
+ mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() => mUserLeaving=" + mUserLeaving);
// If the caller has asked not to resume at this point, we make note
@@ -1455,7 +1499,8 @@
r.delayedResume = true;
}
- ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
+ ActivityRecord notTop =
+ (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
// If the onlyIfNeeded flag is set, then we can do this if the activity
// being launched is the same as the one making the call... or, as
@@ -1478,9 +1523,11 @@
case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
intent.addFlags(
Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ launchFlags = intent.getFlags();
break;
case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+ launchFlags = intent.getFlags();
break;
}
final boolean newDocument = intent.isDocument();
@@ -1609,7 +1656,7 @@
(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
// Caller wants to appear on home activity.
- intentActivity.task.mOnTopOfHome = true;
+ intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
}
options = null;
}
@@ -1786,26 +1833,31 @@
Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
- targetStack = adjustStackFocus(r);
+ newTask = true;
+ targetStack = adjustStackFocus(r, newTask);
targetStack.moveToFront();
if (reuseTask == null) {
r.setTask(targetStack.createTaskRecord(getNextTaskId(),
newTaskInfo != null ? newTaskInfo : r.info,
newTaskIntent != null ? newTaskIntent : intent,
voiceSession, voiceInteractor, true), null, true);
+ if (sourceRecord == null) {
+ // Launched from a service or notification or task that is finishing.
+ r.task.setTaskToReturnTo(isFrontStack(mHomeStack) ?
+ mHomeStack.topTask().taskType : RECENTS_ACTIVITY_TYPE);
+ }
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
r.task);
} else {
r.setTask(reuseTask, reuseTask, true);
}
- newTask = true;
if (!movedHome) {
if ((launchFlags &
(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME))
== (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
// Caller wants to appear on home activity, so before starting
// their own activity we will bring home to the front.
- r.task.mOnTopOfHome = r.task.stack.isOnHomeDisplay();
+ r.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
}
}
} else if (sourceRecord != null) {
@@ -1866,7 +1918,7 @@
// This not being started from an existing activity, and not part
// of a new task... just put it in the top task, though these days
// this case should never happen.
- targetStack = adjustStackFocus(r);
+ targetStack = adjustStackFocus(r, newTask);
targetStack.moveToFront();
ActivityRecord prev = targetStack.topActivity();
r.setTask(prev != null ? prev.task
@@ -2156,7 +2208,7 @@
if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
// Caller wants the home activity moved with it. To accomplish this,
// we'll just indicate that this task returns to the home task.
- task.mOnTopOfHome = true;
+ task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
}
task.stack.moveTaskToFrontLocked(task, null, options);
if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack="
@@ -2250,6 +2302,26 @@
return mLastStackId;
}
+ void createStackForRestoredTaskHistory(ArrayList<TaskRecord> tasks) {
+ int stackId = createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY);
+ final ActivityStack stack = getStack(stackId);
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = tasks.get(taskNdx);
+ stack.addTask(task, false, false);
+ final int taskId = task.taskId;
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ mWindowManager.addAppToken(0, r.appToken, taskId, stackId,
+ r.info.screenOrientation, r.fullscreen,
+ (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
+ r.userId, r.info.configChanges);
+ }
+ mWindowManager.addTask(taskId, stackId, false);
+ }
+ resumeHomeStackTask(HOME_ACTIVITY_TYPE, null);
+ }
+
void moveTaskToStack(int taskId, int stackId, boolean toTop) {
final TaskRecord task = anyTaskForIdLocked(taskId);
if (task == null) {
@@ -2273,7 +2345,12 @@
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (!r.isApplicationActivity() && !stack.isHomeStack()) {
- if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: " + stack);
+ if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: (home activity) " + stack);
+ continue;
+ }
+ if (!stack.mActivityContainer.isEligibleForNewTasks()) {
+ if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: (new task not allowed) " +
+ stack);
continue;
}
final ActivityRecord ar = stack.findTaskLocked(r);
@@ -2504,7 +2581,7 @@
}
} else {
// Stack was moved to another display while user was swapped out.
- resumeHomeActivity(null);
+ resumeHomeStackTask(HOME_ACTIVITY_TYPE, null);
}
return homeInFront;
}
@@ -2897,9 +2974,12 @@
}
void setLockTaskModeLocked(TaskRecord task) {
+ final Message lockTaskMsg = Message.obtain();
if (task == null) {
// Take out of lock task mode.
mLockTaskModeTask = null;
+ lockTaskMsg.what = LOCK_TASK_END_MSG;
+ mHandler.sendMessage(lockTaskMsg);
return;
}
if (isLockTaskModeViolation(task)) {
@@ -2909,6 +2989,8 @@
mLockTaskModeTask = task;
findTaskToMoveToFrontLocked(task, 0, null);
resumeTopActivitiesLocked();
+ lockTaskMsg.what = LOCK_TASK_START_MSG;
+ mHandler.sendMessage(lockTaskMsg);
}
boolean isLockTaskModeViolation(TaskRecord task) {
@@ -3005,6 +3087,32 @@
} catch (RemoteException e) {
}
}
+ case LOCK_TASK_START_MSG: {
+ // When lock task starts, we disable the status bars.
+ try {
+ if (getStatusBarService() != null) {
+ getStatusBarService().disable
+ (StatusBarManager.DISABLE_MASK ^ StatusBarManager.DISABLE_BACK,
+ mToken, mService.mContext.getPackageName());
+ }
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
+ }
+ break;
+ }
+ case LOCK_TASK_END_MSG: {
+ // When lock task ends, we enable the status bars.
+ try {
+ if (getStatusBarService() != null) {
+ getStatusBarService().disable
+ (StatusBarManager.DISABLE_NONE,
+ mToken, mService.mContext.getPackageName());
+ }
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
+ }
+ break;
+ }
}
}
}
@@ -3204,6 +3312,11 @@
void setDrawn() {
}
+ // You can always start a new task on a regular ActivityStack.
+ boolean isEligibleForNewTasks() {
+ return true;
+ }
+
@Override
public String toString() {
return mIdString + (mActivityDisplay == null ? "N" : "A");
@@ -3284,6 +3397,12 @@
}
}
+ // Never start a new task on an ActivityView if it isn't explicitly specified.
+ @Override
+ boolean isEligibleForNewTasks() {
+ return false;
+ }
+
private void setSurfaceIfReady() {
if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReady: mDrawn=" + mDrawn +
" mContainerState=" + mContainerState + " mSurface=" + mSurface);
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
new file mode 100644
index 0000000..3bfaca9
--- /dev/null
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -0,0 +1,353 @@
+/*
+ * 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.am;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Debug;
+import android.os.SystemClock;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class TaskPersister {
+ static final String TAG = "TaskPersister";
+ static final boolean DEBUG = false;
+
+ /** When in slow mode don't write tasks out faster than this */
+ private static final long INTER_TASK_DELAY_MS = 60000;
+ private static final long DEBUG_INTER_TASK_DELAY_MS = 5000;
+
+ private static final String RECENTS_FILENAME = "_task";
+ private static final String TASKS_DIRNAME = "recent_tasks";
+ private static final String TASK_EXTENSION = ".xml";
+ private static final String IMAGES_DIRNAME = "recent_images";
+ private static final String IMAGE_EXTENSION = ".png";
+
+ private static final String TAG_TASK = "task";
+
+ private static File sImagesDir;
+ private static File sTasksDir;
+
+ private final ActivityManagerService mService;
+ private final ActivityStackSupervisor mStackSupervisor;
+
+ private boolean mRecentsChanged = false;
+
+ private final LazyTaskWriterThread mLazyTaskWriterThread;
+
+ TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor) {
+ sTasksDir = new File(systemDir, TASKS_DIRNAME);
+ if (!sTasksDir.exists()) {
+ if (!sTasksDir.mkdir()) {
+ Slog.e(TAG, "Failure creating tasks directory " + sTasksDir);
+ }
+ }
+
+ sImagesDir = new File(systemDir, IMAGES_DIRNAME);
+ if (!sImagesDir.exists()) {
+ if (!sImagesDir.mkdir()) {
+ Slog.e(TAG, "Failure creating images directory " + sImagesDir);
+ }
+ }
+
+ mStackSupervisor = stackSupervisor;
+ mService = stackSupervisor.mService;
+
+ mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
+ }
+
+ void startPersisting() {
+ mLazyTaskWriterThread.start();
+ }
+
+ public void notify(TaskRecord task, boolean flush) {
+ if (DEBUG) Slog.d(TAG, "notify: task=" + task + " flush=" + flush +
+ " Callers=" + Debug.getCallers(4));
+ if (task != null) {
+ task.needsPersisting = true;
+ }
+ synchronized (this) {
+ mLazyTaskWriterThread.mSlow = !flush;
+ mRecentsChanged = true;
+ notifyAll();
+ }
+ }
+
+ private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
+ if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
+ final XmlSerializer xmlSerializer = new FastXmlSerializer();
+ StringWriter stringWriter = new StringWriter();
+ xmlSerializer.setOutput(stringWriter);
+
+ if (DEBUG) xmlSerializer.setFeature(
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ // save task
+ xmlSerializer.startDocument(null, true);
+
+ xmlSerializer.startTag(null, TAG_TASK);
+ task.saveToXml(xmlSerializer);
+ xmlSerializer.endTag(null, TAG_TASK);
+
+ xmlSerializer.endDocument();
+ xmlSerializer.flush();
+
+ return stringWriter;
+ }
+
+ static void saveImage(Bitmap image, String filename) throws IOException {
+ if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename);
+ FileOutputStream imageFile = null;
+ try {
+ imageFile = new FileOutputStream(new File(sImagesDir, filename + IMAGE_EXTENSION));
+ image.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
+ } catch (Exception e) {
+ Slog.e(TAG, "saveImage: unable to save " + filename, e);
+ } finally {
+ if (imageFile != null) {
+ imageFile.close();
+ }
+ }
+ }
+
+ ArrayList<TaskRecord> restoreTasksLocked() {
+ final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
+ ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
+
+ File[] recentFiles = sTasksDir.listFiles();
+ if (recentFiles == null) {
+ Slog.e(TAG, "Unable to list files from " + sTasksDir);
+ return tasks;
+ }
+
+ for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
+ File taskFile = recentFiles[taskNdx];
+ if (DEBUG) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName());
+ BufferedReader reader = null;
+ boolean deleteFile = false;
+ try {
+ reader = new BufferedReader(new FileReader(taskFile));
+ final XmlPullParser in = Xml.newPullParser();
+ in.setInput(reader);
+
+ int event;
+ while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+ event != XmlPullParser.END_TAG) {
+ final String name = in.getName();
+ if (event == XmlPullParser.START_TAG) {
+ if (DEBUG) Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name);
+ if (TAG_TASK.equals(name)) {
+ final TaskRecord task =
+ TaskRecord.restoreFromXml(in, mStackSupervisor);
+ if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" + task);
+ if (task != null) {
+ tasks.add(task);
+ final int taskId = task.taskId;
+ recoveredTaskIds.add(taskId);
+ mStackSupervisor.setNextTaskId(taskId);
+ }
+ } else {
+ Slog.e(TAG, "restoreTasksLocked Unknown xml event=" + event + " name="
+ + name);
+ }
+ }
+ XmlUtils.skipCurrentTag(in);
+ }
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Unable to parse " + taskFile + ". Error " + e);
+ deleteFile = true;
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ }
+ }
+ if (!DEBUG && deleteFile) {
+ taskFile.delete();
+ }
+ }
+ }
+
+ if (!DEBUG) {
+ removeObsoleteFiles(recoveredTaskIds);
+ }
+
+ TaskRecord[] tasksArray = new TaskRecord[tasks.size()];
+ tasks.toArray(tasksArray);
+ Arrays.sort(tasksArray, new Comparator<TaskRecord>() {
+ @Override
+ public int compare(TaskRecord lhs, TaskRecord rhs) {
+ final long diff = lhs.mLastTimeMoved - rhs.mLastTimeMoved;
+ if (diff < 0) {
+ return -1;
+ } else if (diff > 0) {
+ return +1;
+ } else {
+ return 0;
+ }
+ }
+ });
+
+ return new ArrayList<TaskRecord>(Arrays.asList(tasksArray));
+ }
+
+ private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
+ for (int fileNdx = 0; fileNdx < files.length; ++fileNdx) {
+ File file = files[fileNdx];
+ String filename = file.getName();
+ final int taskIdEnd = filename.indexOf('_') + 1;
+ if (taskIdEnd > 0) {
+ final int taskId;
+ try {
+ taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
+ } catch (Exception e) {
+ if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Can't parse file=" +
+ file.getName());
+ file.delete();
+ continue;
+ }
+ if (!persistentTaskIds.contains(taskId)) {
+ if (DEBUG) Slog.d(TAG, "removeObsoleteFile: deleting file=" + file.getName());
+ file.delete();
+ }
+ }
+ }
+ }
+
+ private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) {
+ removeObsoleteFiles(persistentTaskIds, sTasksDir.listFiles());
+ removeObsoleteFiles(persistentTaskIds, sImagesDir.listFiles());
+ }
+
+ static Bitmap restoreImage(String filename) {
+ if (DEBUG) Slog.d(TAG, "restoreImage: restoring " + filename);
+ return BitmapFactory.decodeFile(sImagesDir + File.separator + filename + IMAGE_EXTENSION);
+ }
+
+ private class LazyTaskWriterThread extends Thread {
+ boolean mSlow = true;
+
+ LazyTaskWriterThread(String name) {
+ super(name);
+ }
+
+ @Override
+ public void run() {
+ ArraySet<Integer> persistentTaskIds = new ArraySet<Integer>();
+ while (true) {
+ // If mSlow, then delay between each call to saveToXml().
+ synchronized (TaskPersister.this) {
+ long now = SystemClock.uptimeMillis();
+ final long releaseTime =
+ now + (DEBUG ? DEBUG_INTER_TASK_DELAY_MS: INTER_TASK_DELAY_MS);
+ while (mSlow && now < releaseTime) {
+ try {
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
+ (releaseTime - now));
+ TaskPersister.this.wait(releaseTime - now);
+ } catch (InterruptedException e) {
+ }
+ now = SystemClock.uptimeMillis();
+ }
+ }
+
+ StringWriter stringWriter = null;
+ TaskRecord task = null;
+ synchronized(mService) {
+ final ArrayList<TaskRecord> tasks = mService.mRecentTasks;
+ persistentTaskIds.clear();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ task = tasks.get(taskNdx);
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + " persistable=" +
+ task.isPersistable + " needsPersisting=" + task.needsPersisting);
+ if (task.isPersistable) {
+ persistentTaskIds.add(task.taskId);
+
+ if (task.needsPersisting) {
+ try {
+ stringWriter = saveToXml(task);
+ break;
+ } catch (IOException e) {
+ } catch (XmlPullParserException e) {
+ } finally {
+ task.needsPersisting = false;
+ }
+ }
+ }
+ }
+ }
+
+ if (stringWriter != null) {
+ // Write out xml file while not holding mService lock.
+ FileOutputStream file = null;
+ AtomicFile atomicFile = null;
+ try {
+ atomicFile = new AtomicFile(new File(sTasksDir,
+ String.valueOf(task.taskId) + RECENTS_FILENAME + TASK_EXTENSION));
+ file = atomicFile.startWrite();
+ file.write(stringWriter.toString().getBytes());
+ file.write('\n');
+ atomicFile.finishWrite(file);
+ } catch (IOException e) {
+ if (file != null) {
+ atomicFile.failWrite(file);
+ }
+ Slog.e(TAG, "Unable to open " + atomicFile + " for persisting. " + e);
+ }
+ } else {
+ // Made it through the entire list and didn't find anything new that needed
+ // persisting.
+ if (!DEBUG) {
+ removeObsoleteFiles(persistentTaskIds);
+ }
+
+ // Wait here for someone to call setRecentsChanged().
+ synchronized (TaskPersister.this) {
+ while (!mRecentsChanged) {
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: Waiting.");
+ try {
+ TaskPersister.this.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ mRecentsChanged = false;
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: Awake");
+ }
+ }
+ // Some recents file needs to be written.
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 6d66b29..c07bc1e 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -17,6 +17,9 @@
package com.android.server.am;
import static com.android.server.am.ActivityManagerService.TAG;
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
import android.app.Activity;
@@ -27,15 +30,38 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.util.Slog;
import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
final class TaskRecord extends ThumbnailHolder {
+ private static final String TAG_TASK = "task";
+ private static final String ATTR_TASKID = "task_id";
+ private static final String TAG_INTENT = "intent";
+ private static final String TAG_AFFINITYINTENT = "affinity_intent";
+ private static final String ATTR_REALACTIVITY = "real_activity";
+ private static final String ATTR_ORIGACTIVITY = "orig_activity";
+ private static final String TAG_ACTIVITY = "activity";
+ private static final String ATTR_AFFINITY = "affinity";
+ private static final String ATTR_ROOTHASRESET = "root_has_reset";
+ private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
+ private static final String ATTR_USERID = "user_id";
+ private static final String ATTR_TASKTYPE = "task_type";
+ private static final String ATTR_LASTDESCRIPTION = "last_description";
+ private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
+
+ private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
+
final int taskId; // Unique identifier for this task.
final String affinity; // The affinity name for this task, or null.
final IVoiceInteractionSession voiceSession; // Voice interaction session driving task
@@ -62,25 +88,64 @@
new ActivityManager.TaskDescription();
/** List of all activities in the task arranged in history order */
- final ArrayList<ActivityRecord> mActivities = new ArrayList<ActivityRecord>();
+ final ArrayList<ActivityRecord> mActivities;
/** Current stack */
ActivityStack stack;
/** Takes on same set of values as ActivityRecord.mActivityType */
- private int mTaskType;
+ int taskType;
- /** Launch the home activity when leaving this task. Will be false for tasks that are not on
- * Display.DEFAULT_DISPLAY. */
- boolean mOnTopOfHome = false;
+ /** Takes on same value as first root activity */
+ boolean isPersistable = false;
- TaskRecord(int _taskId, ActivityInfo info, Intent _intent,
+ /** Only used for persistable tasks, otherwise 0. The last time this task was moved. Used for
+ * determining the order when restoring. Sign indicates whether last task movement was to front
+ * (positive) or back (negative). Absolute value indicates time. */
+ long mLastTimeMoved = System.currentTimeMillis();
+
+ /** True if persistable, has changed, and has not yet been persisted */
+ boolean needsPersisting = false;
+
+ /** Indication of what to run next when task exits. Use ActivityRecord types.
+ * ActivityRecord.APPLICATION_ACTIVITY_TYPE indicates to resume the task below this one in the
+ * task stack. */
+ private int mTaskToReturnTo = APPLICATION_ACTIVITY_TYPE;
+
+ final ActivityManagerService mService;
+
+ TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
+ mService = service;
taskId = _taskId;
affinity = info.taskAffinity;
voiceSession = _voiceSession;
voiceInteractor = _voiceInteractor;
setIntent(_intent, info);
+ mActivities = new ArrayList<ActivityRecord>();
+ }
+
+ TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
+ String _affinity, ComponentName _realActivity, ComponentName _origActivity,
+ boolean _rootWasReset, boolean _askedCompatMode, int _taskType, int _userId,
+ String _lastDescription, ArrayList<ActivityRecord> activities, long lastTimeMoved) {
+ mService = service;
+ taskId = _taskId;
+ intent = _intent;
+ affinityIntent = _affinityIntent;
+ affinity = _affinity;
+ voiceSession = null;
+ voiceInteractor = null;
+ realActivity = _realActivity;
+ origActivity = _origActivity;
+ rootWasReset = _rootWasReset;
+ askedCompatMode = _askedCompatMode;
+ taskType = _taskType;
+ mTaskToReturnTo = HOME_ACTIVITY_TYPE;
+ userId = _userId;
+ lastDescription = _lastDescription;
+ mActivities = activities;
+ mLastTimeMoved = lastTimeMoved;
}
void touchActiveTime() {
@@ -144,6 +209,14 @@
}
}
+ void setTaskToReturnTo(int taskToReturnTo) {
+ mTaskToReturnTo = taskToReturnTo;
+ }
+
+ int getTaskToReturnTo() {
+ return mTaskToReturnTo;
+ }
+
void disposeThumbnail() {
super.disposeThumbnail();
for (int i=mActivities.size()-1; i>=0; i--) {
@@ -237,12 +310,16 @@
}
// Only set this based on the first activity
if (mActivities.isEmpty()) {
- mTaskType = r.mActivityType;
+ taskType = r.mActivityType;
+ isPersistable = r.isPersistable();
} else {
// Otherwise make all added activities match this one.
- r.mActivityType = mTaskType;
+ r.mActivityType = taskType;
}
mActivities.add(index, r);
+ if (r.isPersistable()) {
+ mService.notifyTaskPersisterLocked(this, false);
+ }
}
/** @return true if this was the last activity in the task */
@@ -251,6 +328,9 @@
// Was previously in list.
numFullscreen--;
}
+ if (r.isPersistable()) {
+ mService.notifyTaskPersisterLocked(this, false);
+ }
return mActivities.size() == 0;
}
@@ -270,7 +350,14 @@
if (r.finishing) {
continue;
}
- if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", false)) {
+ if (stack == null) {
+ // Task was restored from persistent storage.
+ r.takeFromHistory();
+ mActivities.remove(activityNdx);
+ --activityNdx;
+ --numActivities;
+ } else if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear",
+ false)) {
--activityNdx;
--numActivities;
}
@@ -354,11 +441,13 @@
}
public Bitmap getTaskTopThumbnailLocked() {
- final ActivityRecord resumedActivity = stack.mResumedActivity;
- if (resumedActivity != null && resumedActivity.task == this) {
- // This task is the current resumed task, we just need to take
- // a screenshot of it and return that.
- return stack.screenshotActivities(resumedActivity);
+ if (stack != null) {
+ final ActivityRecord resumedActivity = stack.mResumedActivity;
+ if (resumedActivity != null && resumedActivity.task == this) {
+ // This task is the current resumed task, we just need to take
+ // a screenshot of it and return that.
+ return stack.screenshotActivities(resumedActivity);
+ }
}
// Return the information about the task, to figure out the top
// thumbnail to return.
@@ -399,11 +488,15 @@
}
boolean isHomeTask() {
- return mTaskType == ActivityRecord.HOME_ACTIVITY_TYPE;
+ return taskType == HOME_ACTIVITY_TYPE;
}
boolean isApplicationTask() {
- return mTaskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+ return taskType == APPLICATION_ACTIVITY_TYPE;
+ }
+
+ boolean isOverHomeStack() {
+ return mTaskToReturnTo == HOME_ACTIVITY_TYPE || mTaskToReturnTo == RECENTS_ACTIVITY_TYPE;
}
public TaskAccessInfo getTaskAccessInfoLocked() {
@@ -493,7 +586,7 @@
int activityNdx;
final int numActivities = mActivities.size();
for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
- ++activityNdx) {
+ ++activityNdx) {
final ActivityRecord r = mActivities.get(activityNdx);
if (r.intent != null &&
(r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
@@ -528,14 +621,152 @@
}
}
+ void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+ Slog.i(TAG, "Saving task=" + this);
+
+ out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
+ if (realActivity != null) {
+ out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
+ }
+ if (origActivity != null) {
+ out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
+ }
+ if (affinity != null) {
+ out.attribute(null, ATTR_AFFINITY, affinity);
+ }
+ out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
+ out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
+ out.attribute(null, ATTR_USERID, String.valueOf(userId));
+ out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
+ out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
+ if (lastDescription != null) {
+ out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
+ }
+
+ if (affinityIntent != null) {
+ out.startTag(null, TAG_AFFINITYINTENT);
+ affinityIntent.saveToXml(out);
+ out.endTag(null, TAG_AFFINITYINTENT);
+ }
+
+ out.startTag(null, TAG_INTENT);
+ intent.saveToXml(out);
+ out.endTag(null, TAG_INTENT);
+
+ final ArrayList<ActivityRecord> activities = mActivities;
+ final int numActivities = activities.size();
+ for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (!r.isPersistable() || (r.intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
+ Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) {
+ break;
+ }
+ out.startTag(null, TAG_ACTIVITY);
+ r.saveToXml(out);
+ out.endTag(null, TAG_ACTIVITY);
+ }
+
+ final Bitmap thumbnail = getTaskTopThumbnailLocked();
+ if (thumbnail != null) {
+ TaskPersister.saveImage(thumbnail, String.valueOf(taskId) + TASK_THUMBNAIL_SUFFIX);
+ }
+ }
+
+ static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
+ throws IOException, XmlPullParserException {
+ Intent intent = null;
+ Intent affinityIntent = null;
+ ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
+ ComponentName realActivity = null;
+ ComponentName origActivity = null;
+ String affinity = null;
+ boolean rootHasReset = false;
+ boolean askedCompatMode = false;
+ int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+ int userId = 0;
+ String lastDescription = null;
+ long lastTimeOnTop = 0;
+ int taskId = -1;
+ final int outerDepth = in.getDepth();
+
+ for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
+ final String attrName = in.getAttributeName(attrNdx);
+ final String attrValue = in.getAttributeValue(attrNdx);
+ if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
+ attrName + " value=" + attrValue);
+ if (ATTR_TASKID.equals(attrName)) {
+ taskId = Integer.valueOf(attrValue);
+ } else if (ATTR_REALACTIVITY.equals(attrName)) {
+ realActivity = ComponentName.unflattenFromString(attrValue);
+ } else if (ATTR_ORIGACTIVITY.equals(attrName)) {
+ origActivity = ComponentName.unflattenFromString(attrValue);
+ } else if (ATTR_AFFINITY.equals(attrName)) {
+ affinity = attrValue;
+ } else if (ATTR_ROOTHASRESET.equals(attrName)) {
+ rootHasReset = Boolean.valueOf(attrValue);
+ } else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) {
+ askedCompatMode = Boolean.valueOf(attrValue);
+ } else if (ATTR_USERID.equals(attrName)) {
+ userId = Integer.valueOf(attrValue);
+ } else if (ATTR_TASKTYPE.equals(attrName)) {
+ taskType = Integer.valueOf(attrValue);
+ } else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
+ lastDescription = attrValue;
+ } else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
+ lastTimeOnTop = Long.valueOf(attrValue);
+ } else {
+ Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
+ }
+ }
+
+ int event;
+ while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+ (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
+ if (event == XmlPullParser.START_TAG) {
+ final String name = in.getName();
+ if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" +
+ name);
+ if (TAG_AFFINITYINTENT.equals(name)) {
+ affinityIntent = Intent.restoreFromXml(in);
+ } else if (TAG_INTENT.equals(name)) {
+ intent = Intent.restoreFromXml(in);
+ } else if (TAG_ACTIVITY.equals(name)) {
+ ActivityRecord activity =
+ ActivityRecord.restoreFromXml(in, taskId, stackSupervisor);
+ if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
+ activity);
+ if (activity != null) {
+ activities.add(activity);
+ }
+ } else {
+ Slog.e(TAG, "restoreTask: Unexpected name=" + name);
+ XmlUtils.skipCurrentTag(in);
+ }
+ }
+ }
+
+ final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
+ affinityIntent, affinity, realActivity, origActivity, rootHasReset,
+ askedCompatMode, taskType, userId, lastDescription, activities, lastTimeOnTop);
+
+ for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ r.thumbHolder = r.task = task;
+ }
+
+ task.lastThumbnail = TaskPersister.restoreImage(taskId + TASK_THUMBNAIL_SUFFIX);
+
+ Slog.i(TAG, "Restored task=" + task);
+ return task;
+ }
+
void dump(PrintWriter pw, String prefix) {
- if (numActivities != 0 || rootWasReset || userId != 0 || numFullscreen != 0) {
- pw.print(prefix); pw.print("numActivities="); pw.print(numActivities);
- pw.print(" rootWasReset="); pw.print(rootWasReset);
+ if (rootWasReset || userId != 0 || numFullscreen != 0) {
+ pw.print(prefix); pw.print(" rootWasReset="); pw.print(rootWasReset);
pw.print(" userId="); pw.print(userId);
- pw.print(" mTaskType="); pw.print(mTaskType);
+ pw.print(" taskType="); pw.print(taskType);
pw.print(" numFullscreen="); pw.print(numFullscreen);
- pw.print(" mOnTopOfHome="); pw.println(mOnTopOfHome);
+ pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo);
}
if (affinity != null) {
pw.print(prefix); pw.print("affinity="); pw.println(affinity);
diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java
index 1bc278d..f8e9b7b 100644
--- a/services/core/java/com/android/server/hdmi/FeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/FeatureAction.java
@@ -164,8 +164,13 @@
mActionTimer.sendTimerMessage(state, delayMillis);
}
- protected final boolean sendCommand(HdmiCecMessage cmd) {
- return mService.sendCecCommand(cmd);
+ protected final void sendCommand(HdmiCecMessage cmd) {
+ mService.sendCecCommand(cmd);
+ }
+
+ protected final void sendCommand(HdmiCecMessage cmd,
+ HdmiControlService.SendMessageCallback callback) {
+ mService.sendCecCommand(cmd, callback);
}
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index f99a01d..3c18a59 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -21,10 +21,10 @@
import android.hardware.hdmi.HdmiCecMessage;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseIntArray;
+
+import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
import libcore.util.EmptyArray;
@@ -37,6 +37,9 @@
* and pass it to CEC HAL so that it sends message to other device. For incoming
* message it translates the message and delegates it to proper module.
*
+ * <p>It should be careful to access member variables on IO thread because
+ * it can be accessed from system thread as well.
+ *
* <p>It can be created only by {@link HdmiCecController#create}
*
* <p>Declared as package-private, accessed by {@link HdmiControlService} only.
@@ -58,8 +61,7 @@
private static final int NUM_LOGICAL_ADDRESS = 16;
- // TODO: define other constants for errors.
- private static final int ERROR_SUCCESS = 0;
+ private static final int RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION = 3;
// Handler instance to process synchronous I/O (mainly send) message.
private Handler mIoHandler;
@@ -70,22 +72,19 @@
// Stores the pointer to the native implementation of the service that
// interacts with HAL.
- private long mNativePtr;
+ private volatile long mNativePtr;
private HdmiControlService mService;
- // Map-like container of all cec devices. A logical address of device is
- // used as key of container.
- private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos =
- new SparseArray<HdmiCecDeviceInfo>();
- // Set-like container for all local devices' logical address.
- // Key and value are same.
- private final SparseIntArray mLocalAddresses = new SparseIntArray();
+ // Map-like container of all cec devices including local ones.
+ // A logical address of device is used as key of container.
+ private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
+
+ // Stores the local CEC devices in the system. Device type is used for key.
+ private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
// Private constructor. Use HdmiCecController.create().
private HdmiCecController() {
- // TODO: Consider restoring the local device addresses from persistent storage
- // to allocate the same addresses again if possible.
}
/**
@@ -99,53 +98,42 @@
* returns {@code null}.
*/
static HdmiCecController create(HdmiControlService service) {
- HdmiCecController handler = new HdmiCecController();
- long nativePtr = nativeInit(handler);
+ HdmiCecController controller = new HdmiCecController();
+ long nativePtr = nativeInit(controller);
if (nativePtr == 0L) {
- handler = null;
+ controller = null;
return null;
}
- handler.init(service, nativePtr);
- return handler;
+ controller.init(service, nativePtr);
+ return controller;
+ }
+
+ private void init(HdmiControlService service, long nativePtr) {
+ mService = service;
+ mIoHandler = new Handler(service.getServiceLooper());
+ mControlHandler = new Handler(service.getServiceLooper());
+ mNativePtr = nativePtr;
}
/**
- * Initialize {@link #mLocalAddresses} by allocating logical addresses for each hosted type.
+ * Perform initialization for each hosted device.
*
- * @param deviceTypes local device types
+ * @param deviceTypes array of device types
*/
void initializeLocalDevices(int[] deviceTypes) {
- for (int deviceType : deviceTypes) {
- int preferred = getPreferredAddress(deviceType);
- allocateLogicalAddress(deviceType, preferred, new AllocateLogicalAddressCallback() {
- @Override
- public void onAllocated(int deviceType, int logicalAddress) {
- addLogicalAddress(logicalAddress);
- }
- });
- }
- }
-
- /**
- * Get the preferred address for a given type.
- *
- * @param deviceType logical device type to get the address for
- * @return preferred address; {@link HdmiCec#ADDR_UNREGISTERED} if not available.
- */
- private int getPreferredAddress(int deviceType) {
- // Uses the data restored from persistent memory at boot up if they are available.
- // Otherwise we return UNREGISTERED indicating there is no preferred address.
- // Note that for address SPECIFIC_USE(14), HdmiCec.getTypeFromAddress() returns DEVICE_TV,
- // meaning that we do not support device type video processor yet.
- for (int i = 0; i < mLocalAddresses.size(); ++i) {
- int address = mLocalAddresses.keyAt(i);
- int type = HdmiCec.getTypeFromAddress(address);
- if (type == deviceType) {
- return address;
+ assertRunOnServiceThread();
+ for (int type : deviceTypes) {
+ HdmiCecLocalDevice device = HdmiCecLocalDevice.create(this, type);
+ if (device == null) {
+ continue;
}
+ // TODO: Consider restoring the local device addresses from persistent storage
+ // to allocate the same addresses again if possible.
+ device.setPreferredAddress(HdmiCec.ADDR_UNREGISTERED);
+ mLocalDevices.put(type, device);
+ device.init();
}
- return HdmiCec.ADDR_UNREGISTERED;
}
/**
@@ -176,13 +164,55 @@
* Otherwise, scan address will start from {@code preferredAddress}
* @param callback callback interface to report allocated logical address to caller
*/
- void allocateLogicalAddress(int deviceType, int preferredAddress,
- AllocateLogicalAddressCallback callback) {
- Message msg = mIoHandler.obtainMessage(MSG_ALLOCATE_LOGICAL_ADDRESS);
- msg.arg1 = deviceType;
- msg.arg2 = preferredAddress;
- msg.obj = callback;
- mIoHandler.sendMessage(msg);
+ void allocateLogicalAddress(final int deviceType, final int preferredAddress,
+ final AllocateLogicalAddressCallback callback) {
+ assertRunOnServiceThread();
+
+ runOnIoThread(new Runnable() {
+ @Override
+ public void run() {
+ handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
+ }
+ });
+ }
+
+ private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
+ final AllocateLogicalAddressCallback callback) {
+ assertRunOnIoThread();
+ int startAddress = preferredAddress;
+ // If preferred address is "unregistered", start address will be the smallest
+ // address matched with the given device type.
+ if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) {
+ for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
+ if (deviceType == HdmiCec.getTypeFromAddress(i)) {
+ startAddress = i;
+ break;
+ }
+ }
+ }
+
+ int logicalAddress = HdmiCec.ADDR_UNREGISTERED;
+ // Iterates all possible addresses which has the same device type.
+ for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
+ int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
+ if (curAddress != HdmiCec.ADDR_UNREGISTERED
+ && deviceType == HdmiCec.getTypeFromAddress(curAddress)) {
+ if (!sendPollMessage(curAddress, RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) {
+ logicalAddress = curAddress;
+ break;
+ }
+ }
+ }
+
+ final int assignedAddress = logicalAddress;
+ if (callback != null) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ callback.onAllocated(deviceType, assignedAddress);
+ }
+ });
+ }
}
private static byte[] buildBody(int opcode, byte[] params) {
@@ -192,101 +222,6 @@
return body;
}
- private final class IoHandler extends Handler {
- private IoHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SEND_CEC_COMMAND:
- HdmiCecMessage cecMessage = (HdmiCecMessage) msg.obj;
- byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
- nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
- cecMessage.getDestination(), body);
- break;
- case MSG_ALLOCATE_LOGICAL_ADDRESS:
- int deviceType = msg.arg1;
- int preferredAddress = msg.arg2;
- AllocateLogicalAddressCallback callback =
- (AllocateLogicalAddressCallback) msg.obj;
- handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
- break;
- default:
- Slog.w(TAG, "Unsupported CEC Io request:" + msg.what);
- break;
- }
- }
-
- private void handleAllocateLogicalAddress(int deviceType, int preferredAddress,
- AllocateLogicalAddressCallback callback) {
- int startAddress = preferredAddress;
- // If preferred address is "unregistered", start_index will be the smallest
- // address matched with the given device type.
- if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) {
- for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
- if (deviceType == HdmiCec.getTypeFromAddress(i)) {
- startAddress = i;
- break;
- }
- }
- }
-
- int logcialAddress = HdmiCec.ADDR_UNREGISTERED;
- // Iterates all possible addresses which has the same device type.
- for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
- int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
- if (curAddress != HdmiCec.ADDR_UNREGISTERED
- && deviceType == HdmiCec.getTypeFromAddress(i)) {
- // <Polling Message> is a message which has empty body and
- // uses same address for both source and destination address.
- // If sending <Polling Message> failed (NAK), it becomes
- // new logical address for the device because no device uses
- // it as logical address of the device.
- int error = nativeSendCecCommand(mNativePtr, curAddress, curAddress,
- EMPTY_BODY);
- if (error != ERROR_SUCCESS) {
- logcialAddress = curAddress;
- break;
- }
- }
- }
-
- Message msg = mControlHandler.obtainMessage(MSG_REPORT_LOGICAL_ADDRESS);
- msg.arg1 = deviceType;
- msg.arg2 = logcialAddress;
- msg.obj = callback;
- mControlHandler.sendMessage(msg);
- }
- }
-
- private final class ControlHandler extends Handler {
- private ControlHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_RECEIVE_CEC_COMMAND:
- // TODO: delegate it to HdmiControl service.
- onReceiveCommand((HdmiCecMessage) msg.obj);
- break;
- case MSG_REPORT_LOGICAL_ADDRESS:
- int deviceType = msg.arg1;
- int logicalAddress = msg.arg2;
- AllocateLogicalAddressCallback callback =
- (AllocateLogicalAddressCallback) msg.obj;
- callback.onAllocated(deviceType, logicalAddress);
- break;
- default:
- Slog.i(TAG, "Unsupported message type:" + msg.what);
- break;
- }
- }
- }
-
/**
* Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
* logical address as new device info's.
@@ -298,6 +233,7 @@
* that has the same logical address as new one has.
*/
HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
+ assertRunOnServiceThread();
HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
if (oldDeviceInfo != null) {
removeDeviceInfo(deviceInfo.getLogicalAddress());
@@ -316,6 +252,7 @@
* @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
*/
HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
+ assertRunOnServiceThread();
HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
if (deviceInfo != null) {
mDeviceInfos.remove(logicalAddress);
@@ -324,13 +261,14 @@
}
/**
- * Return a list of all {@HdmiCecDeviceInfo}.
+ * Return a list of all {@link HdmiCecDeviceInfo}.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*/
List<HdmiCecDeviceInfo> getDeviceInfoList() {
- List<HdmiCecDeviceInfo> deviceInfoList = new ArrayList<HdmiCecDeviceInfo>(
- mDeviceInfos.size());
+ assertRunOnServiceThread();
+
+ List<HdmiCecDeviceInfo> deviceInfoList = new ArrayList<>(mDeviceInfos.size());
for (int i = 0; i < mDeviceInfos.size(); ++i) {
deviceInfoList.add(mDeviceInfos.valueAt(i));
}
@@ -347,10 +285,22 @@
* Returns null if no logical address matched
*/
HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
+ assertRunOnServiceThread();
return mDeviceInfos.get(logicalAddress);
}
/**
+ * Return the locally hosted logical device of a given type.
+ *
+ * @param deviceType logical device type
+ * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
+ * otherwise null.
+ */
+ HdmiCecLocalDevice getLocalDevice(int deviceType) {
+ return mLocalDevices.get(deviceType);
+ }
+
+ /**
* Add a new logical address to the device. Device's HW should be notified
* when a new logical address is assigned to a device, so that it can accept
* a command having available destinations.
@@ -361,8 +311,8 @@
* @return 0 on success. Otherwise, returns negative value
*/
int addLogicalAddress(int newLogicalAddress) {
+ assertRunOnServiceThread();
if (HdmiCec.isValidAddress(newLogicalAddress)) {
- mLocalAddresses.put(newLogicalAddress, newLogicalAddress);
return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
} else {
return -1;
@@ -375,9 +325,12 @@
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*/
void clearLogicalAddress() {
+ assertRunOnServiceThread();
// TODO: consider to backup logical address so that new logical address
// allocation can use it as preferred address.
- mLocalAddresses.clear();
+ for (int i = 0; i < mLocalDevices.size(); ++i) {
+ mLocalDevices.valueAt(i).clearAddress();
+ }
nativeClearLogicalAddress(mNativePtr);
}
@@ -390,6 +343,7 @@
* is between 0x0000 and 0xFFFF. If failed it returns -1
*/
int getPhysicalAddress() {
+ assertRunOnServiceThread();
return nativeGetPhysicalAddress(mNativePtr);
}
@@ -399,6 +353,7 @@
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*/
int getVersion() {
+ assertRunOnServiceThread();
return nativeGetVersion(mNativePtr);
}
@@ -408,24 +363,114 @@
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*/
int getVendorId() {
+ assertRunOnServiceThread();
return nativeGetVendorId(mNativePtr);
}
- private void init(HdmiControlService service, long nativePtr) {
- mService = service;
- mIoHandler = new IoHandler(service.getServiceLooper());
- mControlHandler = new ControlHandler(service.getServiceLooper());
- mNativePtr = nativePtr;
+ /**
+ * Poll all remote devices. It sends <Polling Message> to all remote
+ * devices.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param callback an interface used to get a list of all remote devices' address
+ * @param retryCount the number of retry used to send polling message to remote devices
+ */
+ void pollDevices(DevicePollingCallback callback, int retryCount) {
+ assertRunOnServiceThread();
+ // Extract polling candidates. No need to poll against local devices.
+ ArrayList<Integer> pollingCandidates = new ArrayList<>();
+ for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
+ if (!isAllocatedLocalDeviceAddress(i)) {
+ pollingCandidates.add(i);
+ }
+ }
+
+ runDevicePolling(pollingCandidates, retryCount, callback);
+ }
+
+ private boolean isAllocatedLocalDeviceAddress(int address) {
+ for (int i = 0; i < mLocalDevices.size(); ++i) {
+ if (mLocalDevices.valueAt(i).isAddressOf(address)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void runDevicePolling(final List<Integer> candidates, final int retryCount,
+ final DevicePollingCallback callback) {
+ assertRunOnServiceThread();
+ runOnIoThread(new Runnable() {
+ @Override
+ public void run() {
+ final ArrayList<Integer> allocated = new ArrayList<>();
+ for (Integer address : candidates) {
+ if (sendPollMessage(address, retryCount)) {
+ allocated.add(address);
+ }
+ }
+ if (callback != null) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ callback.onPollingFinished(allocated);
+ }
+ });
+ }
+ }
+ });
+ }
+
+ private boolean sendPollMessage(int address, int retryCount) {
+ assertRunOnIoThread();
+ for (int i = 0; i < retryCount; ++i) {
+ // <Polling Message> is a message which has empty body and
+ // uses same address for both source and destination address.
+ // If sending <Polling Message> failed (NAK), it becomes
+ // new logical address for the device because no device uses
+ // it as logical address of the device.
+ if (nativeSendCecCommand(mNativePtr, address, address, EMPTY_BODY)
+ == HdmiControlService.SEND_RESULT_SUCCESS) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void assertRunOnIoThread() {
+ if (Looper.myLooper() != mIoHandler.getLooper()) {
+ throw new IllegalStateException("Should run on io thread.");
+ }
+ }
+
+ private void assertRunOnServiceThread() {
+ if (Looper.myLooper() != mControlHandler.getLooper()) {
+ throw new IllegalStateException("Should run on service thread.");
+ }
+ }
+
+ // Run a Runnable on IO thread.
+ // It should be careful to access member variables on IO thread because
+ // it can be accessed from system thread as well.
+ private void runOnIoThread(Runnable runnable) {
+ mIoHandler.post(runnable);
+ }
+
+ private void runOnServiceThread(Runnable runnable) {
+ mControlHandler.post(runnable);
}
private boolean isAcceptableAddress(int address) {
- // Can access command targeting devices available in local device or
- // broadcast command.
- return address == HdmiCec.ADDR_BROADCAST
- || mLocalAddresses.indexOfKey(address) < 0;
+ // Can access command targeting devices available in local device or broadcast command.
+ if (address == HdmiCec.ADDR_BROADCAST) {
+ return true;
+ }
+ return isAllocatedLocalDeviceAddress(address);
}
private void onReceiveCommand(HdmiCecMessage message) {
+ assertRunOnServiceThread();
if (isAcceptableAddress(message.getDestination()) &&
mService.handleCecCommand(message)) {
return;
@@ -438,13 +483,34 @@
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand
(sourceAddress, message.getSource(), message.getOpcode(),
HdmiCecMessageBuilder.ABORT_REFUSED);
- sendCommand(cecMessage);
-
+ sendCommand(cecMessage, null);
}
- boolean sendCommand(HdmiCecMessage cecMessage) {
- Message message = mIoHandler.obtainMessage(MSG_SEND_CEC_COMMAND, cecMessage);
- return mIoHandler.sendMessage(message);
+ void sendCommand(HdmiCecMessage cecMessage) {
+ sendCommand(cecMessage, null);
+ }
+
+ void sendCommand(final HdmiCecMessage cecMessage,
+ final HdmiControlService.SendMessageCallback callback) {
+ runOnIoThread(new Runnable() {
+ @Override
+ public void run() {
+ byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
+ final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
+ cecMessage.getDestination(), body);
+ if (error != HdmiControlService.SEND_RESULT_SUCCESS) {
+ Slog.w(TAG, "Failed to send " + cecMessage);
+ }
+ if (callback != null) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ callback.onSendCompleted(error);
+ }
+ });
+ }
+ }
+ });
}
/**
@@ -453,12 +519,15 @@
private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
byte opcode = body[0];
byte params[] = Arrays.copyOfRange(body, 1, body.length);
- HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
+ final HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
// Delegate message to main handler so that it handles in main thread.
- Message message = mControlHandler.obtainMessage(
- MSG_RECEIVE_CEC_COMMAND, cecMessage);
- mControlHandler.sendMessage(message);
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ onReceiveCommand(cecMessage);
+ }
+ });
}
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
new file mode 100644
index 0000000..e65e5fa
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -0,0 +1,103 @@
+/*
+ * 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.hdmi;
+
+import com.android.server.hdmi.HdmiCecController.AllocateLogicalAddressCallback;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+
+/**
+ * Class that models a logical CEC device hosted in this system. Handles initialization,
+ * CEC commands that call for actions customized per device type.
+ */
+abstract class HdmiCecLocalDevice {
+
+ protected final HdmiCecController mController;
+ protected final int mDeviceType;
+ protected int mAddress;
+ protected int mPreferredAddress;
+ protected HdmiCecDeviceInfo mDeviceInfo;
+
+ protected HdmiCecLocalDevice(HdmiCecController controller, int deviceType) {
+ mController = controller;
+ mDeviceType = deviceType;
+ mAddress = HdmiCec.ADDR_UNREGISTERED;
+ }
+
+ // Factory method that returns HdmiCecLocalDevice of corresponding type.
+ static HdmiCecLocalDevice create(HdmiCecController controller, int deviceType) {
+ switch (deviceType) {
+ case HdmiCec.DEVICE_TV:
+ return new HdmiCecLocalDeviceTv(controller);
+ case HdmiCec.DEVICE_PLAYBACK:
+ return new HdmiCecLocalDevicePlayback(controller);
+ default:
+ return null;
+ }
+ }
+
+ abstract void init();
+
+ protected void allocateAddress(int type) {
+ mController.allocateLogicalAddress(type, mPreferredAddress,
+ new AllocateLogicalAddressCallback() {
+ @Override
+ public void onAllocated(int deviceType, int logicalAddress) {
+ mAddress = mPreferredAddress = logicalAddress;
+
+ // Create and set device info.
+ HdmiCecDeviceInfo deviceInfo = createDeviceInfo(mAddress, deviceType);
+ setDeviceInfo(deviceInfo);
+ mController.addDeviceInfo(deviceInfo);
+
+ mController.addLogicalAddress(logicalAddress);
+ }
+ });
+ }
+
+ private final HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
+ int vendorId = mController.getVendorId();
+ int physicalAddress = mController.getPhysicalAddress();
+ // TODO: get device name read from system configuration.
+ String displayName = HdmiCec.getDefaultDeviceName(logicalAddress);
+ return new HdmiCecDeviceInfo(logicalAddress,
+ physicalAddress, deviceType, vendorId, displayName);
+ }
+
+ HdmiCecDeviceInfo getDeviceInfo() {
+ return mDeviceInfo;
+ }
+
+ void setDeviceInfo(HdmiCecDeviceInfo info) {
+ mDeviceInfo = info;
+ }
+
+ // Returns true if the logical address is same as the argument.
+ boolean isAddressOf(int addr) {
+ return addr == mAddress;
+ }
+
+ // Resets the logical address to unregistered(15), meaning the logical device is invalid.
+ void clearAddress() {
+ mAddress = HdmiCec.ADDR_UNREGISTERED;
+ }
+
+ void setPreferredAddress(int addr) {
+ mPreferredAddress = addr;
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
new file mode 100644
index 0000000..a953467
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.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 com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+
+/**
+ * Represent a logical device of type Playback residing in Android system.
+ */
+final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
+
+ HdmiCecLocalDevicePlayback(HdmiCecController controller) {
+ super(controller, HdmiCec.DEVICE_PLAYBACK);
+ }
+
+ @Override
+ void init() {
+ allocateAddress(mDeviceType);
+ mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ mAddress, mController.getPhysicalAddress(), mDeviceType));
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
new file mode 100644
index 0000000..01ea685
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -0,0 +1,43 @@
+/*
+ * 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.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+
+/**
+ * Represent a logical device of type TV residing in Android system.
+ */
+final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
+
+ HdmiCecLocalDeviceTv(HdmiCecController controller) {
+ super(controller, HdmiCec.DEVICE_TV);
+ }
+
+ @Override
+ void init() {
+ allocateAddress(mDeviceType);
+
+ // TODO: vendor-specific initialization here.
+
+ mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ mAddress, mController.getPhysicalAddress(), mDeviceType));
+ mController.sendCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
+ mAddress, mController.getVendorId()));
+
+ // TODO: Start routing control action, device discovery action.
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 09153b9..475852f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -18,27 +18,23 @@
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.hdmi.IHdmiControlCallback;
-import android.hardware.hdmi.IHdmiControlService;
-import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.hdmi.IHdmiControlService;
+import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Slog;
-import android.util.SparseArray;
-
import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -54,6 +50,37 @@
// TODO: Rename the permission to HDMI_CONTROL.
private static final String PERMISSION = "android.permission.HDMI_CEC";
+ static final int SEND_RESULT_SUCCESS = 0;
+ static final int SEND_RESULT_NAK = -1;
+ static final int SEND_RESULT_FAILURE = -2;
+
+ /**
+ * Interface to report send result.
+ */
+ interface SendMessageCallback {
+ /**
+ * Called when {@link HdmiControlService#sendCecCommand} is completed.
+ *
+ * @param error result of send request.
+ * @see {@link #SEND_RESULT_SUCCESS}
+ * @see {@link #SEND_RESULT_NAK}
+ * @see {@link #SEND_RESULT_FAILURE}
+ */
+ void onSendCompleted(int error);
+ }
+
+ /**
+ * Interface to get a list of available logical devices.
+ */
+ interface DevicePollingCallback {
+ /**
+ * Called when device polling is finished.
+ *
+ * @param ackedAddress a list of logical addresses of available devices
+ */
+ void onPollingFinished(List<Integer> ackedAddress);
+ }
+
// A thread to handle synchronous IO of CEC and MHL control service.
// Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
// and sparse call it shares a thread to handle IO operations.
@@ -151,6 +178,16 @@
});
}
+ // See if we have an action of a given type in progress.
+ private <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
+ for (FeatureAction action : mActions) {
+ if (action.getClass().equals(clazz)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Remove the given {@link FeatureAction} object from the action queue.
*
@@ -205,10 +242,14 @@
* Transmit a CEC command to CEC bus.
*
* @param command CEC command to send out
- * @return {@code true} if succeeds to send command
+ * @param callback interface used to the result of send command
*/
- boolean sendCecCommand(HdmiCecMessage command) {
- return mCecController.sendCommand(command);
+ void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+ mCecController.sendCommand(command, callback);
+ }
+
+ void sendCecCommand(HdmiCecMessage command) {
+ mCecController.sendCommand(command, null);
}
/**
@@ -263,6 +304,17 @@
// TODO: Start "RequestArcInitiationAction" if ARC port.
}
+ /**
+ * Poll all remote devices. It sends <Polling Message> to all remote
+ * devices.
+ *
+ * @param callback an interface used to get a list of all remote devices' address
+ * @param retryCount the number of retry used to send polling message to remote devices
+ */
+ void pollDevices(DevicePollingCallback callback, int retryCount) {
+ mCecController.pollDevices(callback, retryCount);
+ }
+
private void handleInitiateArc(HdmiCecMessage message){
// In case where <Initiate Arc> is started by <Request ARC Initiation>
// need to clean up RequestArcInitiationAction.
@@ -373,36 +425,95 @@
}
@Override
- public void oneTouchPlay(IHdmiControlCallback callback) {
+ public void oneTouchPlay(final IHdmiControlCallback callback) {
enforceAccessPermission();
- // TODO: Post a message for HdmiControlService#oneTouchPlay()
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiControlService.this.oneTouchPlay(callback);
+ }
+ });
}
@Override
- public void queryDisplayStatus(IHdmiControlCallback callback) {
+ public void queryDisplayStatus(final IHdmiControlCallback callback) {
enforceAccessPermission();
- // TODO: Post a message for HdmiControlService#queryDisplayStatus()
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiControlService.this.queryDisplayStatus(callback);
+ }
+ });
}
@Override
- public void addHotplugEventListener(IHdmiHotplugEventListener listener) {
+ public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
enforceAccessPermission();
- // TODO: Post a message for HdmiControlService#addHotplugEventListener()
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiControlService.this.addHotplugEventListener(listener);
+ }
+ });
}
@Override
- public void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
+ public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
enforceAccessPermission();
- // TODO: Post a message for HdmiControlService#removeHotplugEventListener()
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiControlService.this.removeHotplugEventListener(listener);
+ }
+ });
}
}
private void oneTouchPlay(IHdmiControlCallback callback) {
- // TODO: Create a new action
+ if (hasAction(OneTouchPlayAction.class)) {
+ Slog.w(TAG, "oneTouchPlay already in progress");
+ invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+ return;
+ }
+ HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
+ if (source == null) {
+ Slog.w(TAG, "Local playback device not available");
+ invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
+ return;
+ }
+ // TODO: Consider the case of multiple TV sets. For now we always direct the command
+ // to the primary one.
+ OneTouchPlayAction action = OneTouchPlayAction.create(this,
+ source.getDeviceInfo().getLogicalAddress(),
+ source.getDeviceInfo().getPhysicalAddress(), HdmiCec.ADDR_TV, callback);
+ if (action == null) {
+ Slog.w(TAG, "Cannot initiate oneTouchPlay");
+ invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+ return;
+ }
+ addAndStartAction(action);
}
private void queryDisplayStatus(IHdmiControlCallback callback) {
- // TODO: Create a new action
+ if (hasAction(DevicePowerStatusAction.class)) {
+ Slog.w(TAG, "queryDisplayStatus already in progress");
+ invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+ return;
+ }
+ HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
+ if (source == null) {
+ Slog.w(TAG, "Local playback device not available");
+ invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
+ return;
+ }
+ DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
+ source.getDeviceInfo().getLogicalAddress(), HdmiCec.ADDR_TV, callback);
+ if (action == null) {
+ Slog.w(TAG, "Cannot initiate queryDisplayStatus");
+ invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+ return;
+ }
+ addAndStartAction(action);
}
private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
@@ -431,4 +542,12 @@
mHotplugEventListeners.remove(listener);
}
}
+
+ private void invokeCallback(IHdmiControlCallback callback, int result) {
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Invoking callback failed:" + e);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
index 0a701f9..343aff7 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
@@ -16,7 +16,7 @@
package com.android.server.hdmi;
-import android.util.Slog;
+import android.hardware.hdmi.HdmiCecMessage;
/**
* Feature action that handles ARC action initiated by TV devices.
@@ -37,17 +37,22 @@
@Override
boolean start() {
- if (sendCommand(
- HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress, mAvrAddress))) {
- mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
- addTimer(mState, TIMEOUT_MS);
- } else {
- Slog.w(TAG, "Failed to send <Request ARC Initiation>");
- // If failed to send <Request ARC Initiation>, start "Disabled" ARC transmission
- // action.
- disableArcTransmission();
- finish();
- }
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress,
+ mAvrAddress);
+ sendCommand(command, new HdmiControlService.SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
+ addTimer(mState, TIMEOUT_MS);
+ } else {
+ // If failed to send <Request ARC Initiation>, start "Disabled"
+ // ARC transmission action.
+ disableArcTransmission();
+ finish();
+ }
+ }
+ });
return true;
}
}
diff --git a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
index db1b992..d4a35f8 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
@@ -16,7 +16,7 @@
package com.android.server.hdmi;
-import android.util.Slog;
+import android.hardware.hdmi.HdmiCecMessage;
/**
* Feature action to handle <Request ARC Termination>.
@@ -37,17 +37,22 @@
@Override
boolean start() {
- if (sendCommand(
- HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress))) {
- mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
- addTimer(mState, TIMEOUT_MS);
- } else {
- Slog.w(TAG, "Failed to send <Request ARC Initiation>");
- // If failed to send <Request ARC Termination>, start "Disabled" ARC transmission
- // action.
- disableArcTransmission();
- finish();
- }
+ HdmiCecMessage command =
+ HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress);
+ sendCommand(command, new HdmiControlService.SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
+ addTimer(mState, TIMEOUT_MS);
+ } else {
+ // If failed to send <Request ARC Termination>, start "Disabled" ARC
+ // transmission action.
+ disableArcTransmission();
+ finish();
+ }
+ }
+ });
return true;
}
}
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index 6bf149b..e3525d8 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -64,26 +64,7 @@
@Override
boolean start() {
if (mEnabled) {
- if (sendCommand(
- HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress))) {
- // Enable ARC status immediately after sending <Report Arc Initiated>.
- // If AVR responds with <Feature Abort>, disable ARC status again.
- // This is different from spec that says that turns ARC status to "Enabled"
- // if <Report ARC Initiated> is acknowledged and no <Feature Abort> is received.
- // But implemented this way to save the time having to wait for <Feature Abort>.
- setArcStatus(true);
- // If succeeds to send <Report ARC Initiated>, wait general timeout
- // to check whether there is no <Feature Abort> for <Report ARC Initiated>.
- mState = STATE_WAITING_TIMEOUT;
- addTimer(mState, TIMEOUT_MS);
- } else {
- // If fails to send <Report ARC Initiated>, disable ARC and
- // send <Report ARC Terminated> directly.
- Slog.w(TAG, "Failed to send <Report ARC Initiated>:[source:" + mSourceAddress
- + ", avr Address:" + mAvrAddress + "]");
- setArcStatus(false);
- finish();
- }
+ sendReportArcInitiated();
} else {
setArcStatus(false);
finish();
@@ -91,6 +72,35 @@
return true;
}
+ private void sendReportArcInitiated() {
+ HdmiCecMessage command =
+ HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress);
+ sendCommand(command, new HdmiControlService.SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ // Enable ARC status immediately after sending <Report Arc Initiated>.
+ // If AVR responds with <Feature Abort>, disable ARC status again.
+ // This is different from spec that says that turns ARC status to
+ // "Enabled" if <Report ARC Initiated> is acknowledged and no
+ // <Feature Abort> is received.
+ // But implemented this way to save the time having to wait for
+ // <Feature Abort>.
+ setArcStatus(true);
+ // If succeeds to send <Report ARC Initiated>, wait general timeout
+ // to check whether there is no <Feature Abort> for <Report ARC Initiated>.
+ mState = STATE_WAITING_TIMEOUT;
+ addTimer(mState, TIMEOUT_MS);
+ } else {
+ // If fails to send <Report ARC Initiated>, disable ARC and
+ // send <Report ARC Terminated> directly.
+ setArcStatus(false);
+ finish();
+ }
+ }
+ });
+ }
+
private void setArcStatus(boolean enabled) {
boolean wasEnabled = mService.setArcStatus(enabled);
Slog.i(TAG, "Change arc status [old:" + wasEnabled + " ,new:" + enabled);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 584145f..b94ea62 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -175,7 +175,10 @@
public void registerService(IInterface service, ComponentName component, int userid) {
checkNotNull(service);
- registerServiceImpl(service, component, userid);
+ ManagedServiceInfo info = registerServiceImpl(service, component, userid);
+ if (info != null) {
+ onServiceAdded(info);
+ }
}
/**
@@ -464,7 +467,7 @@
}
}
- private void registerServiceImpl(final IInterface service,
+ private ManagedServiceInfo registerServiceImpl(final IInterface service,
final ComponentName component, final int userid) {
synchronized (mMutex) {
try {
@@ -472,10 +475,12 @@
true /*isSystem*/, null, Build.VERSION_CODES.L);
service.asBinder().linkToDeath(info, 0);
mServices.add(info);
+ return info;
} catch (RemoteException e) {
// already dead
}
}
+ return null;
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index 125158fd..db17f3a 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.util.Slog;
-import com.android.internal.R;
import com.android.server.notification.NotificationManagerService.NotificationRecord;
/**
@@ -39,7 +38,7 @@
if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
}
- public RankingFuture process(NotificationRecord record) {
+ public RankingReconsideration process(NotificationRecord record) {
if (record == null || record.getNotification() == null) {
if (DBG) Slog.d(TAG, "skipping empty notification");
return null;
@@ -54,10 +53,15 @@
record.setRecentlyIntusive(true);
}
- return new RankingFuture(record, HANG_TIME_MS) {
+ return new RankingReconsideration(record.getKey(), HANG_TIME_MS) {
@Override
public void work() {
- mRecord.setRecentlyIntusive(false);
+ // pass
+ }
+
+ @Override
+ public void applyChangesLocked(NotificationRecord record) {
+ record.setRecentlyIntusive(false);
}
};
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b9a69ab..bbc3091 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -111,7 +111,6 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/** {@hide} */
@@ -453,8 +452,7 @@
- public static final class NotificationRecord
- {
+ public static final class NotificationRecord {
final StatusBarNotification sbn;
SingleNotificationStats stats;
boolean isCanceled;
@@ -556,13 +554,13 @@
return mContactAffinity;
}
- public boolean isRecentlyIntrusive() {
- return mRecentlyIntrusive;
- }
-
public void setRecentlyIntusive(boolean recentlyIntrusive) {
mRecentlyIntrusive = recentlyIntrusive;
}
+
+ public boolean isRecentlyIntrusive() {
+ return mRecentlyIntrusive;
+ }
}
private static final class ToastRecord
@@ -1264,7 +1262,7 @@
@Override
public void registerListener(final INotificationListener listener,
final ComponentName component, final int userid) {
- checkCallerIsSystem();
+ enforceSystemOrSystemUI("INotificationManager.registerListener");
mListeners.registerService(listener, component, userid);
}
@@ -1635,8 +1633,8 @@
if (!mSignalExtractors.isEmpty()) {
for (NotificationSignalExtractor extractor : mSignalExtractors) {
try {
- RankingFuture future = extractor.process(r);
- scheduleRankingReconsideration(future);
+ RankingReconsideration recon = extractor.process(r);
+ scheduleRankingReconsideration(recon);
} catch (Throwable t) {
Slog.w(TAG, "NotificationSignalExtractor failed.", t);
}
@@ -1982,37 +1980,38 @@
}
}
- private void scheduleRankingReconsideration(RankingFuture future) {
- if (future != null) {
- Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, future);
- long delay = future.getDelay(TimeUnit.MILLISECONDS);
+ private void scheduleRankingReconsideration(RankingReconsideration recon) {
+ if (recon != null) {
+ Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, recon);
+ long delay = recon.getDelay(TimeUnit.MILLISECONDS);
mRankingHandler.sendMessageDelayed(m, delay);
}
}
private void handleRankingReconsideration(Message message) {
- if (!(message.obj instanceof RankingFuture)) return;
-
- RankingFuture future = (RankingFuture) message.obj;
- future.run();
- try {
- NotificationRecord record = future.get();
- synchronized (mNotificationList) {
- int before = mNotificationList.indexOf(record);
- if (before != -1) {
- Collections.sort(mNotificationList, mRankingComparator);
- int after = mNotificationList.indexOf(record);
-
- if (before != after) {
- scheduleSendRankingUpdate();
- }
- }
+ if (!(message.obj instanceof RankingReconsideration)) return;
+ RankingReconsideration recon = (RankingReconsideration) message.obj;
+ recon.run();
+ boolean orderChanged;
+ synchronized (mNotificationList) {
+ final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
+ if (record == null) {
+ return;
}
- } catch (InterruptedException e) {
- // we're running the future explicitly, so this should never happen
- } catch (ExecutionException e) {
- // we're running the future explicitly, so this should never happen
+ int before = findNotificationRecordIndexLocked(record);
+ recon.applyChangesLocked(record);
+ Collections.sort(mNotificationList, mRankingComparator);
+ int after = findNotificationRecordIndexLocked(record);
+ orderChanged = before != after;
}
+ if (orderChanged) {
+ scheduleSendRankingUpdate();
+ }
+ }
+
+ // lock on mNotificationList
+ private int findNotificationRecordIndexLocked(NotificationRecord target) {
+ return Collections.binarySearch(mNotificationList, target, mRankingComparator);
}
private void scheduleSendRankingUpdate() {
diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
index a41fdfe..71c819e 100644
--- a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
@@ -18,6 +18,8 @@
import android.content.Context;
+import com.android.server.notification.NotificationManagerService.NotificationRecord;
+
/**
* Extracts signals that will be useful to the {@link NotificationComparator} and caches them
* on the {@link NotificationManagerService.NotificationRecord} object. These annotations will
@@ -32,10 +34,10 @@
* Called once per notification that is posted or updated.
*
* @return null if the work is done, or a future if there is more to do. The
- * {@link RankingFuture} will be run on a worker thread, and if notifications are re-ordered
- * by that execution, the {@link NotificationManagerService} may send order update
- * events to the {@link android.service.notification.NotificationListenerService}s.
+ * {@link RankingReconsideration} will be run on a worker thread, and if notifications
+ * are re-ordered by that execution, the {@link NotificationManagerService} may send order
+ * update events to the {@link android.service.notification.NotificationListenerService}s.
*/
- public RankingFuture process(NotificationManagerService.NotificationRecord notification);
+ public RankingReconsideration process(NotificationRecord notification);
}
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index d81a25e..009943f 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -46,12 +46,14 @@
* {@hide}
*/
public class NotificationUsageStats {
+ private static final boolean ENABLE_SQLITE_LOG = true;
+
// Guarded by synchronized(this).
private final Map<String, AggregatedStats> mStats = new HashMap<String, AggregatedStats>();
private final SQLiteLog mSQLiteLog;
public NotificationUsageStats(Context context) {
- mSQLiteLog = new SQLiteLog(context);
+ mSQLiteLog = ENABLE_SQLITE_LOG ? new SQLiteLog(context) : null;
}
/**
@@ -63,7 +65,9 @@
for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
stats.numPostedByApp++;
}
- mSQLiteLog.logPosted(notification);
+ if (ENABLE_SQLITE_LOG) {
+ mSQLiteLog.logPosted(notification);
+ }
}
/**
@@ -85,7 +89,9 @@
stats.numRemovedByApp++;
stats.collect(notification.stats);
}
- mSQLiteLog.logRemoved(notification);
+ if (ENABLE_SQLITE_LOG) {
+ mSQLiteLog.logRemoved(notification);
+ }
}
/**
@@ -97,7 +103,9 @@
stats.numDismissedByUser++;
stats.collect(notification.stats);
}
- mSQLiteLog.logDismissed(notification);
+ if (ENABLE_SQLITE_LOG) {
+ mSQLiteLog.logDismissed(notification);
+ }
}
/**
@@ -108,7 +116,9 @@
for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
stats.numClickedByUser++;
}
- mSQLiteLog.logClicked(notification);
+ if (ENABLE_SQLITE_LOG) {
+ mSQLiteLog.logClicked(notification);
+ }
}
/**
@@ -164,7 +174,9 @@
for (AggregatedStats as : mStats.values()) {
as.dump(pw, indent);
}
- mSQLiteLog.dump(pw, indent);
+ if (ENABLE_SQLITE_LOG) {
+ mSQLiteLog.dump(pw, indent);
+ }
}
/**
diff --git a/services/core/java/com/android/server/notification/RankingFuture.java b/services/core/java/com/android/server/notification/RankingFuture.java
deleted file mode 100644
index d711d02..0000000
--- a/services/core/java/com/android/server/notification/RankingFuture.java
+++ /dev/null
@@ -1,118 +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.notification;
-
-import java.util.concurrent.Delayed;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-public abstract class RankingFuture
- implements ScheduledFuture<NotificationManagerService.NotificationRecord> {
- private static final long IMMEDIATE = 0l;
-
- private static final int START = 0;
- private static final int RUNNING = 1;
- private static final int DONE = 2;
- private static final int CANCELLED = 3;
-
- private int mState;
- private long mDelay;
- protected NotificationManagerService.NotificationRecord mRecord;
-
- public RankingFuture(NotificationManagerService.NotificationRecord record) {
- this(record, IMMEDIATE);
- }
-
- public RankingFuture(NotificationManagerService.NotificationRecord record, long delay) {
- mDelay = delay;
- mRecord = record;
- mState = START;
- }
-
- public void run() {
- if (mState == START) {
- mState = RUNNING;
-
- work();
-
- mState = DONE;
- synchronized (this) {
- notifyAll();
- }
- }
- }
-
- @Override
- public long getDelay(TimeUnit unit) {
- return unit.convert(mDelay, TimeUnit.MILLISECONDS);
- }
-
- @Override
- public int compareTo(Delayed another) {
- return Long.compare(getDelay(TimeUnit.MILLISECONDS),
- another.getDelay(TimeUnit.MILLISECONDS));
- }
-
- @Override
- public boolean cancel(boolean mayInterruptIfRunning) {
- if (mState == START) { // can't cancel if running or done
- mState = CANCELLED;
- return true;
- }
- return false;
- }
-
- @Override
- public boolean isCancelled() {
- return mState == CANCELLED;
- }
-
- @Override
- public boolean isDone() {
- return mState == DONE;
- }
-
- @Override
- public NotificationManagerService.NotificationRecord get()
- throws InterruptedException, ExecutionException {
- while (!isDone()) {
- synchronized (this) {
- this.wait();
- }
- }
- return mRecord;
- }
-
- @Override
- public NotificationManagerService.NotificationRecord get(long timeout, TimeUnit unit)
- throws InterruptedException, ExecutionException, TimeoutException {
- long timeoutMillis = unit.convert(timeout, TimeUnit.MILLISECONDS);
- long start = System.currentTimeMillis();
- long now = System.currentTimeMillis();
- while (!isDone() && (now - start) < timeoutMillis) {
- try {
- wait(timeoutMillis - (now - start));
- } catch (InterruptedException e) {
- now = System.currentTimeMillis();
- }
- }
- return mRecord;
- }
-
- public abstract void work();
-}
diff --git a/services/core/java/com/android/server/notification/RankingReconsideration.java b/services/core/java/com/android/server/notification/RankingReconsideration.java
new file mode 100644
index 0000000..cf5e210
--- /dev/null
+++ b/services/core/java/com/android/server/notification/RankingReconsideration.java
@@ -0,0 +1,99 @@
+/*
+ * 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 com.android.server.notification.NotificationManagerService.NotificationRecord;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Represents future work required to extract signals from notifications for ranking.
+ *
+ * {@hide}
+ */
+public abstract class RankingReconsideration implements Runnable {
+ private static final long IMMEDIATE = 0l;
+
+ private static final int START = 0;
+ private static final int RUNNING = 1;
+ private static final int DONE = 2;
+ private static final int CANCELLED = 3;
+
+ private int mState;
+ private long mDelay;
+ protected String mKey;
+
+ public RankingReconsideration(String key) {
+ this(key, IMMEDIATE);
+ }
+
+ public RankingReconsideration(String key, long delay) {
+ mDelay = delay;
+ mKey = key;
+ mState = START;
+ }
+
+ public String getKey() {
+ return mKey;
+ }
+
+ public void run() {
+ if (mState == START) {
+ mState = RUNNING;
+
+ work();
+
+ mState = DONE;
+ synchronized (this) {
+ notifyAll();
+ }
+ }
+ }
+
+ public long getDelay(TimeUnit unit) {
+ return unit.convert(mDelay, TimeUnit.MILLISECONDS);
+ }
+
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ if (mState == START) { // can't cancel if running or done
+ mState = CANCELLED;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isCancelled() {
+ return mState == CANCELLED;
+ }
+
+ public boolean isDone() {
+ return mState == DONE;
+ }
+
+ /**
+ * Analyse the notification. This will be called on a worker thread. To
+ * avoid concurrency issues, do not use held references to modify the
+ * {@link NotificationRecord}.
+ */
+ public abstract void work();
+
+ /**
+ * Apply any computed changes to the notification record. This method will be
+ * called on the main service thread, synchronized on he mNotificationList.
+ * @param record The locked record to be updated.
+ */
+ public abstract void applyChangesLocked(NotificationRecord record);
+}
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index b5c2730..157d749 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -59,7 +59,7 @@
// maps raw person handle to resolved person object
private LruCache<String, LookupResult> mPeopleCache;
- private RankingFuture validatePeople(NotificationRecord record) {
+ private RankingReconsideration validatePeople(final NotificationRecord record) {
float affinity = NONE;
Bundle extras = record.getNotification().extras;
if (extras == null) {
@@ -99,11 +99,11 @@
}
if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + record.sbn.getKey());
- return new RankingFuture(record) {
+ return new RankingReconsideration(record.getKey()) {
+ float mContactAffinity = NONE;
@Override
public void work() {
- if (INFO) Slog.i(TAG, "Executing: validation for: " + mRecord.sbn.getKey());
- float affinity = NONE;
+ if (INFO) Slog.i(TAG, "Executing: validation for: " + record.getKey());
for (final String handle: pendingLookups) {
LookupResult lookupResult = null;
final Uri uri = Uri.parse(handle);
@@ -124,13 +124,16 @@
synchronized (mPeopleCache) {
mPeopleCache.put(handle, lookupResult);
}
- affinity = Math.max(affinity, lookupResult.getAffinity());
+ mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
}
}
- float affinityBound = mRecord.getContactAffinity();
- affinity = Math.max(affinity, affinityBound);
- mRecord.setContactAffinity(affinity);
- if (INFO) Slog.i(TAG, "final affinity: " + affinity);
+ }
+
+ @Override
+ public void applyChangesLocked(NotificationRecord operand) {
+ float affinityBound = operand.getContactAffinity();
+ operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
+ if (INFO) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
}
};
}
@@ -229,7 +232,7 @@
mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
}
- public RankingFuture process(NotificationManagerService.NotificationRecord record) {
+ public RankingReconsideration process(NotificationRecord record) {
if (!mEnabled) {
if (INFO) Slog.i(TAG, "disabled");
return null;
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
new file mode 100644
index 0000000..3d432dc
--- /dev/null
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
@@ -0,0 +1,118 @@
+/*
+ * 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.server.pm;
+
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+import android.content.IntentFilter;
+import android.util.Log;
+import java.io.IOException;
+import android.os.UserHandle;
+
+/**
+ * The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user.
+ * If an {@link Intent} matches the {@link CrossProfileIntentFilter}, then activities in the user
+ * {@link #mTargetUserId} can access it.
+ */
+class CrossProfileIntentFilter extends IntentFilter {
+ private static final String ATTR_TARGET_USER_ID = "targetUserId";
+ private static final String ATTR_USER_ID_DEST = "userIdDest";//Old name. Kept for compatibility.
+ private static final String ATTR_REMOVABLE = "removable";
+ private static final String ATTR_FILTER = "filter";
+
+ private static final String TAG = "CrossProfileIntentFilter";
+
+ // If the intent matches the IntentFilter, then it can be forwarded to this userId.
+ final int mTargetUserId;
+ boolean mRemovable;
+
+ CrossProfileIntentFilter(IntentFilter filter, boolean removable, int targetUserId) {
+ super(filter);
+ mTargetUserId = targetUserId;
+ mRemovable = removable;
+ }
+
+ public int getTargetUserId() {
+ return mTargetUserId;
+ }
+
+ public boolean isRemovable() {
+ return mRemovable;
+ }
+
+ CrossProfileIntentFilter(XmlPullParser parser) throws XmlPullParserException, IOException {
+ String targetUserIdString = parser.getAttributeValue(null, ATTR_TARGET_USER_ID);
+ if (targetUserIdString == null) {
+ targetUserIdString = parser.getAttributeValue(null, ATTR_USER_ID_DEST);
+ }
+ if (targetUserIdString == null) {
+ String msg = "Missing element under " + TAG +": " + ATTR_TARGET_USER_ID + " at " +
+ parser.getPositionDescription();
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ mTargetUserId = UserHandle.USER_NULL;
+ } else {
+ mTargetUserId = Integer.parseInt(targetUserIdString);
+ }
+ String removableString = parser.getAttributeValue(null, ATTR_REMOVABLE);
+ if (removableString != null) {
+ mRemovable = Boolean.parseBoolean(removableString);
+ }
+ int outerDepth = parser.getDepth();
+ String tagName = parser.getName();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ tagName = parser.getName();
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ } else if (type == XmlPullParser.START_TAG) {
+ if (tagName.equals(ATTR_FILTER)) {
+ break;
+ } else {
+ String msg = "Unknown element under " + Settings.TAG_FORWARDING_INTENT_FILTERS
+ + ": " + tagName + " at " + parser.getPositionDescription();
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+ if (tagName.equals(ATTR_FILTER)) {
+ readFromXml(parser);
+ } else {
+ String msg = "Missing element under " + TAG + ": " + ATTR_FILTER +
+ " at " + parser.getPositionDescription();
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ public void writeToXml(XmlSerializer serializer) throws IOException {
+ serializer.attribute(null, ATTR_TARGET_USER_ID, Integer.toString(mTargetUserId));
+ serializer.attribute(null, ATTR_REMOVABLE, Boolean.toString(mRemovable));
+ serializer.startTag(null, ATTR_FILTER);
+ super.writeToXml(serializer);
+ serializer.endTag(null, ATTR_FILTER);
+ }
+
+ @Override
+ public String toString() {
+ return "CrossProfileIntentFilter{0x" + Integer.toHexString(System.identityHashCode(this))
+ + " " + Integer.toString(mTargetUserId) + "}";
+ }
+}
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java
new file mode 100644
index 0000000..a335d3a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java
@@ -0,0 +1,44 @@
+/*
+ * 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.server.pm;
+
+
+import java.io.PrintWriter;
+import com.android.server.IntentResolver;
+import java.util.List;
+
+/**
+ * Used to find a list of {@link CrossProfileIntentFilter}s that match an intent.
+ */
+class CrossProfileIntentResolver
+ extends IntentResolver<CrossProfileIntentFilter, CrossProfileIntentFilter> {
+ @Override
+ protected CrossProfileIntentFilter[] newArray(int size) {
+ return new CrossProfileIntentFilter[size];
+ }
+
+ @Override
+ protected boolean isPackageForFilter(String packageName, CrossProfileIntentFilter filter) {
+ return false;
+ }
+
+ @Override
+ protected void sortResults(List<CrossProfileIntentFilter> results) {
+ //We don't sort the results
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ForwardingIntentFilter.java b/services/core/java/com/android/server/pm/ForwardingIntentFilter.java
deleted file mode 100644
index 85bde98..0000000
--- a/services/core/java/com/android/server/pm/ForwardingIntentFilter.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.server.pm;
-
-import com.android.internal.util.XmlUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-import android.content.IntentFilter;
-import android.util.Log;
-import java.io.IOException;
-import android.os.UserHandle;
-
-/**
- * The {@link PackageManagerService} maintains some {@link ForwardingIntentFilter}s for every user.
- * If an {@link Intent} matches the {@link ForwardingIntentFilter}, then it can be forwarded to the
- * {@link #mUserIdDest}.
- */
-class ForwardingIntentFilter extends IntentFilter {
- private static final String ATTR_USER_ID_DEST = "userIdDest";
- private static final String ATTR_REMOVABLE = "removable";
- private static final String ATTR_FILTER = "filter";
-
- private static final String TAG = "ForwardingIntentFilter";
-
- // If the intent matches the IntentFilter, then it can be forwarded to this userId.
- final int mUserIdDest;
- boolean mRemovable;
-
- ForwardingIntentFilter(IntentFilter filter, boolean removable, int userIdDest) {
- super(filter);
- mUserIdDest = userIdDest;
- mRemovable = removable;
- }
-
- public int getUserIdDest() {
- return mUserIdDest;
- }
-
- public boolean isRemovable() {
- return mRemovable;
- }
-
- ForwardingIntentFilter(XmlPullParser parser) throws XmlPullParserException, IOException {
- String userIdDestString = parser.getAttributeValue(null, ATTR_USER_ID_DEST);
- if (userIdDestString == null) {
- String msg = "Missing element under " + TAG +": " + ATTR_USER_ID_DEST + " at " +
- parser.getPositionDescription();
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- mUserIdDest = UserHandle.USER_NULL;
- } else {
- mUserIdDest = Integer.parseInt(userIdDestString);
- }
- String removableString = parser.getAttributeValue(null, ATTR_REMOVABLE);
- if (removableString != null) {
- mRemovable = Boolean.parseBoolean(removableString);
- }
- int outerDepth = parser.getDepth();
- String tagName = parser.getName();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- tagName = parser.getName();
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- } else if (type == XmlPullParser.START_TAG) {
- if (tagName.equals(ATTR_FILTER)) {
- break;
- } else {
- String msg = "Unknown element under " + Settings.TAG_FORWARDING_INTENT_FILTERS
- + ": " + tagName + " at " + parser.getPositionDescription();
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- XmlUtils.skipCurrentTag(parser);
- }
- }
- }
- if (tagName.equals(ATTR_FILTER)) {
- readFromXml(parser);
- } else {
- String msg = "Missing element under " + TAG + ": " + ATTR_FILTER +
- " at " + parser.getPositionDescription();
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
- public void writeToXml(XmlSerializer serializer) throws IOException {
- serializer.attribute(null, ATTR_USER_ID_DEST, Integer.toString(mUserIdDest));
- serializer.attribute(null, ATTR_REMOVABLE, Boolean.toString(mRemovable));
- serializer.startTag(null, ATTR_FILTER);
- super.writeToXml(serializer);
- serializer.endTag(null, ATTR_FILTER);
- }
-
- @Override
- public String toString() {
- return "ForwardingIntentFilter{0x" + Integer.toHexString(System.identityHashCode(this))
- + " " + Integer.toString(mUserIdDest) + "}";
- }
-}
diff --git a/services/core/java/com/android/server/pm/ForwardingIntentResolver.java b/services/core/java/com/android/server/pm/ForwardingIntentResolver.java
deleted file mode 100644
index 1616395..0000000
--- a/services/core/java/com/android/server/pm/ForwardingIntentResolver.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.server.pm;
-
-
-import java.io.PrintWriter;
-import com.android.server.IntentResolver;
-import java.util.List;
-
-/**
- * Used to find a list of {@link ForwardingIntentFilter}s that match an intent.
- */
-class ForwardingIntentResolver
- extends IntentResolver<ForwardingIntentFilter, ForwardingIntentFilter> {
- @Override
- protected ForwardingIntentFilter[] newArray(int size) {
- return new ForwardingIntentFilter[size];
- }
-
- @Override
- protected boolean isPackageForFilter(String packageName, ForwardingIntentFilter filter) {
- return false;
- }
-
- @Override
- protected void sortResults(List<ForwardingIntentFilter> results) {
- //We don't sort the results
- }
-}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index f90d7ab..3ed73f7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -32,6 +32,7 @@
import android.content.pm.Signature;
import android.os.Build;
import android.os.Bundle;
+import android.os.FileBridge;
import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
@@ -114,7 +115,7 @@
private boolean mPermissionsConfirmed;
private boolean mInvalid;
- private ArrayList<WritePipe> mPipes = new ArrayList<>();
+ private ArrayList<FileBridge> mBridges = new ArrayList<>();
private IPackageInstallObserver2 mRemoteObserver;
@@ -159,14 +160,14 @@
// Quick sanity check of state, and allocate a pipe for ourselves. We
// then do heavy disk allocation outside the lock, but this open pipe
// will block any attempted install transitions.
- final WritePipe pipe;
+ final FileBridge bridge;
synchronized (mLock) {
if (!mMutationsAllowed) {
throw new IllegalStateException("Mutations not allowed");
}
- pipe = new WritePipe();
- mPipes.add(pipe);
+ bridge = new FileBridge();
+ mBridges.add(bridge);
}
try {
@@ -194,9 +195,9 @@
Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
}
- pipe.setTargetFd(targetFd);
- pipe.start();
- return pipe.getWriteFd();
+ bridge.setTargetFile(targetFd);
+ bridge.start();
+ return new ParcelFileDescriptor(bridge.getClientSocket());
} catch (ErrnoException e) {
throw new IllegalStateException("Failed to write", e);
@@ -218,8 +219,8 @@
// Verify that all writers are hands-off
if (mMutationsAllowed) {
- for (WritePipe pipe : mPipes) {
- if (!pipe.isClosed()) {
+ for (FileBridge bridge : mBridges) {
+ if (!bridge.isClosed()) {
throw new InstallFailedException(INSTALL_FAILED_PACKAGE_CHANGED,
"Files still open");
}
@@ -482,52 +483,6 @@
}
}
- private static class WritePipe extends Thread {
- private final ParcelFileDescriptor[] mPipe;
-
- private FileDescriptor mTargetFd;
-
- private volatile boolean mClosed;
-
- public WritePipe() {
- try {
- mPipe = ParcelFileDescriptor.createPipe();
- } catch (IOException e) {
- throw new IllegalStateException("Failed to create pipe");
- }
- }
-
- public boolean isClosed() {
- return mClosed;
- }
-
- public void setTargetFd(FileDescriptor targetFd) {
- mTargetFd = targetFd;
- }
-
- public ParcelFileDescriptor getWriteFd() {
- return mPipe[1];
- }
-
- @Override
- public void run() {
- FileInputStream in = null;
- FileOutputStream out = null;
- try {
- // TODO: look at switching to sendfile(2) to speed up
- in = new FileInputStream(mPipe[0].getFileDescriptor());
- out = new FileOutputStream(mTargetFd);
- Streams.copy(in, out);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to stream data: " + e);
- } finally {
- IoUtils.closeQuietly(mPipe[0]);
- IoUtils.closeQuietly(mTargetFd);
- mClosed = true;
- }
- }
- }
-
private class InstallFailedException extends Exception {
private final int error;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0fa0b14..6d75ee5 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3374,25 +3374,26 @@
* Returns if intent can be forwarded from the userId from to dest
*/
@Override
- public boolean canForwardTo(Intent intent, String resolvedType, int userIdFrom, int userIdDest) {
+ public boolean canForwardTo(Intent intent, String resolvedType, int sourceUserId,
+ int targetUserId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
- List<ForwardingIntentFilter> matches =
- getMatchingForwardingIntentFilters(intent, resolvedType, userIdFrom);
+ List<CrossProfileIntentFilter> matches =
+ getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId);
if (matches != null) {
int size = matches.size();
for (int i = 0; i < size; i++) {
- if (matches.get(i).getUserIdDest() == userIdDest) return true;
+ if (matches.get(i).getTargetUserId() == targetUserId) return true;
}
}
return false;
}
- private List<ForwardingIntentFilter> getMatchingForwardingIntentFilters(Intent intent,
+ private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
String resolvedType, int userId) {
- ForwardingIntentResolver fir = mSettings.mForwardingIntentResolvers.get(userId);
- if (fir != null) {
- return fir.queryIntent(intent, resolvedType, false, userId);
+ CrossProfileIntentResolver cpir = mSettings.mCrossProfileIntentResolvers.get(userId);
+ if (cpir != null) {
+ return cpir.queryIntent(intent, resolvedType, false, userId);
}
return null;
}
@@ -3428,31 +3429,31 @@
List<ResolveInfo> result =
mActivities.queryIntent(intent, resolvedType, flags, userId);
// Checking if we can forward the intent to another user
- List<ForwardingIntentFilter> fifs =
- getMatchingForwardingIntentFilters(intent, resolvedType, userId);
- if (fifs != null) {
- ForwardingIntentFilter forwardingIntentFilterWithResult = null;
+ List<CrossProfileIntentFilter> cpifs =
+ getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
+ if (cpifs != null) {
+ CrossProfileIntentFilter crossProfileIntentFilterWithResult = null;
HashSet<Integer> alreadyTriedUserIds = new HashSet<Integer>();
- for (ForwardingIntentFilter fif : fifs) {
- int userIdDest = fif.getUserIdDest();
- // Two {@link ForwardingIntentFilter}s can have the same userIdDest and
+ for (CrossProfileIntentFilter cpif : cpifs) {
+ int targetUserId = cpif.getTargetUserId();
+ // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and
// match the same an intent. For performance reasons, it is better not to
// run queryIntent twice for the same userId
- if (!alreadyTriedUserIds.contains(userIdDest)) {
+ if (!alreadyTriedUserIds.contains(targetUserId)) {
List<ResolveInfo> resultUser = mActivities.queryIntent(intent,
- resolvedType, flags, userIdDest);
+ resolvedType, flags, targetUserId);
if (resultUser != null) {
- forwardingIntentFilterWithResult = fif;
+ crossProfileIntentFilterWithResult = cpif;
// As soon as there is a match in another user, we add the
// intentForwarderActivity to the list of ResolveInfo.
break;
}
- alreadyTriedUserIds.add(userIdDest);
+ alreadyTriedUserIds.add(targetUserId);
}
}
- if (forwardingIntentFilterWithResult != null) {
+ if (crossProfileIntentFilterWithResult != null) {
ResolveInfo forwardingResolveInfo = createForwardingResolveInfo(
- forwardingIntentFilterWithResult, userId);
+ crossProfileIntentFilterWithResult, userId);
result.add(forwardingResolveInfo);
}
}
@@ -3467,10 +3468,11 @@
}
}
- private ResolveInfo createForwardingResolveInfo(ForwardingIntentFilter fif, int userIdFrom) {
+ private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter cpif,
+ int sourceUserId) {
String className;
- int userIdDest = fif.getUserIdDest();
- if (userIdDest == UserHandle.USER_OWNER) {
+ int targetUserId = cpif.getTargetUserId();
+ if (targetUserId == UserHandle.USER_OWNER) {
className = FORWARD_INTENT_TO_USER_OWNER;
} else {
className = FORWARD_INTENT_TO_MANAGED_PROFILE;
@@ -3478,14 +3480,14 @@
ComponentName forwardingActivityComponentName = new ComponentName(
mAndroidApplication.packageName, className);
ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0,
- userIdFrom);
+ sourceUserId);
ResolveInfo forwardingResolveInfo = new ResolveInfo();
forwardingResolveInfo.activityInfo = forwardingActivityInfo;
forwardingResolveInfo.priority = 0;
forwardingResolveInfo.preferredOrder = 0;
forwardingResolveInfo.match = 0;
forwardingResolveInfo.isDefault = true;
- forwardingResolveInfo.filter = fif;
+ forwardingResolveInfo.filter = cpif;
return forwardingResolveInfo;
}
@@ -6140,7 +6142,12 @@
}
final String nativeLibraryPath = (new File(libDir, apkName)).getPath();
pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath;
- pkgSetting.nativeLibraryPathString = nativeLibraryPath;
+ // pkgSetting might be null during rescan following uninstall of updates
+ // to a bundled app, so accommodate that possibility. The settings in
+ // that case will be established later from the parsed package.
+ if (pkgSetting != null) {
+ pkgSetting.nativeLibraryPathString = nativeLibraryPath;
+ }
}
// Deduces the required ABI of an upgraded system app.
@@ -11470,33 +11477,34 @@
}
@Override
- public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int userIdOrig,
- int userIdDest) {
+ public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable,
+ int sourceUserId, int targetUserId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
if (filter.countActions() == 0) {
- Slog.w(TAG, "Cannot set a forwarding intent filter with no filter actions");
+ Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions");
return;
}
synchronized (mPackages) {
- mSettings.editForwardingIntentResolverLPw(userIdOrig).addFilter(
- new ForwardingIntentFilter(filter, removable, userIdDest));
- mSettings.writePackageRestrictionsLPr(userIdOrig);
+ mSettings.editCrossProfileIntentResolverLPw(sourceUserId).addFilter(
+ new CrossProfileIntentFilter(filter, removable, targetUserId));
+ mSettings.writePackageRestrictionsLPr(sourceUserId);
}
}
@Override
- public void clearForwardingIntentFilters(int userIdOrig) {
+ public void clearCrossProfileIntentFilters(int sourceUserId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
synchronized (mPackages) {
- ForwardingIntentResolver fir = mSettings.editForwardingIntentResolverLPw(userIdOrig);
- HashSet<ForwardingIntentFilter> set =
- new HashSet<ForwardingIntentFilter>(fir.filterSet());
- for (ForwardingIntentFilter fif : set) {
- if (fif.isRemovable()) fir.removeFilter(fif);
+ CrossProfileIntentResolver cpir =
+ mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
+ HashSet<CrossProfileIntentFilter> set =
+ new HashSet<CrossProfileIntentFilter>(cpir.filterSet());
+ for (CrossProfileIntentFilter cpif : set) {
+ if (cpif.isRemovable()) cpir.removeFilter(cpif);
}
- mSettings.writePackageRestrictionsLPr(userIdOrig);
+ mSettings.writePackageRestrictionsLPr(sourceUserId);
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3ca658f..bb92611 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -132,6 +132,9 @@
private static final String TAG_PACKAGE = "pkg";
private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES =
"persistent-preferred-activities";
+ static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
+ "crossProfile-intent-filters";
+ //Old name. Kept for compatibility
static final String TAG_FORWARDING_INTENT_FILTERS =
"forwarding-intent-filters";
@@ -189,8 +192,8 @@
new SparseArray<PersistentPreferredIntentResolver>();
// For every user, it is used to find to which other users the intent can be forwarded.
- final SparseArray<ForwardingIntentResolver> mForwardingIntentResolvers =
- new SparseArray<ForwardingIntentResolver>();
+ final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers =
+ new SparseArray<CrossProfileIntentResolver>();
final HashMap<String, SharedUserSetting> mSharedUsers =
new HashMap<String, SharedUserSetting>();
@@ -856,13 +859,13 @@
return ppir;
}
- ForwardingIntentResolver editForwardingIntentResolverLPw(int userId) {
- ForwardingIntentResolver fir = mForwardingIntentResolvers.get(userId);
- if (fir == null) {
- fir = new ForwardingIntentResolver();
- mForwardingIntentResolvers.put(userId, fir);
+ CrossProfileIntentResolver editCrossProfileIntentResolverLPw(int userId) {
+ CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(userId);
+ if (cpir == null) {
+ cpir = new CrossProfileIntentResolver();
+ mCrossProfileIntentResolvers.put(userId, cpir);
}
- return fir;
+ return cpir;
}
private File getUserPackagesStateFile(int userId) {
@@ -980,7 +983,7 @@
}
}
- private void readForwardingIntentFiltersLPw(XmlPullParser parser, int userId)
+ private void readCrossProfileIntentFiltersLPw(XmlPullParser parser, int userId)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -991,10 +994,10 @@
}
String tagName = parser.getName();
if (tagName.equals(TAG_ITEM)) {
- ForwardingIntentFilter fif = new ForwardingIntentFilter(parser);
- editForwardingIntentResolverLPw(userId).addFilter(fif);
+ CrossProfileIntentFilter cpif = new CrossProfileIntentFilter(parser);
+ editCrossProfileIntentResolverLPw(userId).addFilter(cpif);
} else {
- String msg = "Unknown element under " + TAG_FORWARDING_INTENT_FILTERS + ": " +
+ String msg = "Unknown element under " + TAG_CROSS_PROFILE_INTENT_FILTERS + ": " +
parser.getName();
PackageManagerService.reportSettingsProblem(Log.WARN, msg);
XmlUtils.skipCurrentTag(parser);
@@ -1130,8 +1133,9 @@
readPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
readPersistentPreferredActivitiesLPw(parser, userId);
- } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)) {
- readForwardingIntentFiltersLPw(parser, userId);
+ } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)
+ || tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {
+ readCrossProfileIntentFiltersLPw(parser, userId);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
+ parser.getName());
@@ -1209,18 +1213,18 @@
serializer.endTag(null, TAG_PERSISTENT_PREFERRED_ACTIVITIES);
}
- void writeForwardingIntentFiltersLPr(XmlSerializer serializer, int userId)
+ void writeCrossProfileIntentFiltersLPr(XmlSerializer serializer, int userId)
throws IllegalArgumentException, IllegalStateException, IOException {
- serializer.startTag(null, TAG_FORWARDING_INTENT_FILTERS);
- ForwardingIntentResolver fir = mForwardingIntentResolvers.get(userId);
- if (fir != null) {
- for (final ForwardingIntentFilter fif : fir.filterSet()) {
+ serializer.startTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS);
+ CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(userId);
+ if (cpir != null) {
+ for (final CrossProfileIntentFilter cpif : cpir.filterSet()) {
serializer.startTag(null, TAG_ITEM);
- fif.writeToXml(serializer);
+ cpif.writeToXml(serializer);
serializer.endTag(null, TAG_ITEM);
}
}
- serializer.endTag(null, TAG_FORWARDING_INTENT_FILTERS);
+ serializer.endTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS);
}
void writePackageRestrictionsLPr(int userId) {
@@ -1321,7 +1325,7 @@
writePersistentPreferredActivitiesLPr(serializer, userId);
- writeForwardingIntentFiltersLPr(serializer, userId);
+ writeCrossProfileIntentFiltersLPr(serializer, userId);
serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
@@ -1943,7 +1947,7 @@
} else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)) {
// TODO: check whether this is okay! as it is very
// similar to how preferred-activities are treated
- readForwardingIntentFiltersLPw(parser, 0);
+ readCrossProfileIntentFiltersLPw(parser, 0);
} else if (tagName.equals("updated-package")) {
readDisabledSysPackageLPw(parser);
} else if (tagName.equals("cleaning-package")) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 102b2d4..1bf40e0 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1140,53 +1140,57 @@
*/
public boolean removeUser(int userHandle) {
checkManageUsersPermission("Only the system can remove users");
- final UserInfo user;
- synchronized (mPackagesLock) {
- user = mUsers.get(userHandle);
- if (userHandle == 0 || user == null) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ final UserInfo user;
+ synchronized (mPackagesLock) {
+ user = mUsers.get(userHandle);
+ if (userHandle == 0 || user == null) {
+ return false;
+ }
+ mRemovingUserIds.put(userHandle, true);
+ try {
+ mAppOpsService.removeUser(userHandle);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
+ }
+ // Set this to a partially created user, so that the user will be purged
+ // on next startup, in case the runtime stops now before stopping and
+ // removing the user completely.
+ user.partial = true;
+ // Mark it as disabled, so that it isn't returned any more when
+ // profiles are queried.
+ user.flags |= UserInfo.FLAG_DISABLED;
+ writeUserLocked(user);
+ }
+
+ if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
+ && user.isManagedProfile()) {
+ // Send broadcast to notify system that the user removed was a
+ // managed user.
+ sendProfileRemovedBroadcast(user.profileGroupId, user.id);
+ }
+
+ if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
+ int res;
+ try {
+ res = ActivityManagerNative.getDefault().stopUser(userHandle,
+ new IStopUserCallback.Stub() {
+ @Override
+ public void userStopped(int userId) {
+ finishRemoveUser(userId);
+ }
+ @Override
+ public void userStopAborted(int userId) {
+ }
+ });
+ } catch (RemoteException e) {
return false;
}
- mRemovingUserIds.put(userHandle, true);
- try {
- mAppOpsService.removeUser(userHandle);
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
- }
- // Set this to a partially created user, so that the user will be purged
- // on next startup, in case the runtime stops now before stopping and
- // removing the user completely.
- user.partial = true;
- // Mark it as disabled, so that it isn't returned any more when
- // profiles are queried.
- user.flags |= UserInfo.FLAG_DISABLED;
- writeUserLocked(user);
+ return res == ActivityManager.USER_OP_SUCCESS;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
-
- if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
- && user.isManagedProfile()) {
- // Send broadcast to notify system that the user removed was a
- // managed user.
- sendProfileRemovedBroadcast(user.profileGroupId, user.id);
- }
-
- if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
- int res;
- try {
- res = ActivityManagerNative.getDefault().stopUser(userHandle,
- new IStopUserCallback.Stub() {
- @Override
- public void userStopped(int userId) {
- finishRemoveUser(userId);
- }
- @Override
- public void userStopAborted(int userId) {
- }
- });
- } catch (RemoteException e) {
- return false;
- }
-
- return res == ActivityManager.USER_OP_SUCCESS;
}
void finishRemoveUser(final int userHandle) {
diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java
index 6d208ff..80030b4 100644
--- a/services/core/java/com/android/server/task/TaskManagerService.java
+++ b/services/core/java/com/android/server/task/TaskManagerService.java
@@ -16,12 +16,22 @@
package com.android.server.task;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.task.ITaskManager;
+import android.app.task.Task;
import android.content.Context;
-import android.content.Task;
+import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.util.Log;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.server.task.controllers.TaskStatus;
@@ -39,13 +49,6 @@
/** Master list of tasks. */
private final TaskStore mTasks;
- /** Check the pending queue and start any tasks. */
- static final int MSG_RUN_PENDING = 0;
- /** Initiate the stop task flow. */
- static final int MSG_STOP_TASK = 1;
- /** */
- static final int MSG_CHECK_TASKS = 2;
-
/**
* Track Services that have currently active or pending tasks. The index is provided by
* {@link TaskStatus#getServiceToken()}
@@ -54,6 +57,14 @@
new SparseArray<TaskServiceContext>();
private final TaskHandler mHandler;
+ private final TaskManagerStub mTaskManagerStub;
+
+ /** Check the pending queue and start any tasks. */
+ static final int MSG_RUN_PENDING = 0;
+ /** Initiate the stop task flow. */
+ static final int MSG_STOP_TASK = 1;
+ /** */
+ static final int MSG_CHECK_TASKS = 2;
private class TaskHandler extends Handler {
@@ -94,21 +105,6 @@
}
/**
- * Entry point from client to schedule the provided task.
- * This will add the task to the
- * @param task Task object containing execution parameters
- * @param userId The id of the user this task is for.
- * @param uId The package identifier of the application this task is for.
- * @param canPersistTask Whether or not the client has the appropriate permissions for persisting
- * of this task.
- * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
- */
- public int schedule(Task task, int userId, int uId, boolean canPersistTask) {
- TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask);
- return 0;
- }
-
- /**
* Initializes the system service.
* <p>
* Subclasses must define a single argument constructor that accepts the context
@@ -121,11 +117,42 @@
super(context);
mTasks = new TaskStore(context);
mHandler = new TaskHandler(context.getMainLooper());
+ mTaskManagerStub = new TaskManagerStub();
}
@Override
public void onStart() {
+ publishBinderService(Context.TASK_SERVICE, mTaskManagerStub);
+ }
+ /**
+ * Entry point from client to schedule the provided task.
+ * This will add the task to the
+ * @param task Task object containing execution parameters
+ * @param userId The id of the user this task is for.
+ * @param uId The package identifier of the application this task is for.
+ * @param canPersistTask Whether or not the client has the appropriate permissions for
+ * persisting of this task.
+ * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
+ */
+ public int schedule(Task task, int userId, int uId, boolean canPersistTask) {
+ TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask);
+ return 0;
+ }
+
+ public List<Task> getPendingTasks(int uid) {
+ ArrayList<Task> outList = new ArrayList<Task>(3);
+ synchronized (mTasks) {
+ final SparseArray<TaskStatus> tasks = mTasks.getTasks();
+ final int N = tasks.size();
+ for (int i = 0; i < N; i++) {
+ TaskStatus ts = tasks.get(i);
+ if (ts.getUid() == uid) {
+ outList.add(ts.getTask());
+ }
+ }
+ }
+ return outList;
}
// StateChangedListener implementations.
@@ -162,7 +189,7 @@
public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule) {
final TaskServiceContext serviceContext = mActiveServices.get(serviceToken);
if (serviceContext == null) {
- Log.e(TAG, "Task completed for invalid service context; " + serviceToken);
+ Slog.e(TAG, "Task completed for invalid service context; " + serviceToken);
return;
}
@@ -203,4 +230,98 @@
private void postCheckTasksMessage() {
mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
}
+
+ /**
+ * Binder stub trampoline implementation
+ */
+ final class TaskManagerStub extends ITaskManager.Stub {
+ /** Cache determination of whether a given app can persist tasks
+ * key is uid of the calling app; value is undetermined/true/false
+ */
+ private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
+
+ // Determine whether the caller is allowed to persist tasks, with a small cache
+ // because the lookup is expensive enough that we'd like to avoid repeating it.
+ // This must be called from within the calling app's binder identity!
+ private boolean canCallerPersistTasks() {
+ final boolean canPersist;
+ final int callingUid = Binder.getCallingUid();
+ synchronized (mPersistCache) {
+ Boolean cached = mPersistCache.get(callingUid);
+ if (cached) {
+ canPersist = cached.booleanValue();
+ } else {
+ // Persisting tasks is tantamount to running at boot, so we permit
+ // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
+ // permission
+ int result = getContext().checkCallingPermission(
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED);
+ canPersist = (result == PackageManager.PERMISSION_GRANTED);
+ mPersistCache.put(callingUid, canPersist);
+ }
+ }
+ return canPersist;
+ }
+
+ // ITaskManager implementation
+ @Override
+ public int schedule(Task task) throws RemoteException {
+ final boolean canPersist = canCallerPersistTasks();
+ final int uid = Binder.getCallingUid();
+ final int userId = UserHandle.getCallingUserId();
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return TaskManagerService.this.schedule(task, userId, uid, canPersist);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public List<Task> getAllPendingTasks() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void cancelAll() throws RemoteException {
+ }
+
+ @Override
+ public void cancel(int taskId) throws RemoteException {
+ }
+
+ /**
+ * "dumpsys" infrastructure
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+
+ long identityToken = Binder.clearCallingIdentity();
+ try {
+ TaskManagerService.this.dumpInternal(pw);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+ };
+
+ void dumpInternal(PrintWriter pw) {
+ synchronized (mTasks) {
+ pw.print("Registered tasks:");
+ if (mTasks.size() > 0) {
+ SparseArray<TaskStatus> tasks = mTasks.getTasks();
+ for (int i = 0; i < tasks.size(); i++) {
+ TaskStatus ts = tasks.get(i);
+ pw.println();
+ ts.dump(pw, " ");
+ }
+ } else {
+ pw.println();
+ pw.println("No tasks scheduled.");
+ }
+ }
+ pw.println();
+ }
}
diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java
index b51cbb3..165445a 100644
--- a/services/core/java/com/android/server/task/TaskServiceContext.java
+++ b/services/core/java/com/android/server/task/TaskServiceContext.java
@@ -45,7 +45,7 @@
* is reused to start concurrent tasks on the TaskService. Information here is unique
* to the service.
* Functionality provided by this class:
- * - Managages wakelock for the service.
+ * - Manages wakelock for the service.
* - Sends onStartTask() and onStopTask() messages to client app, and handles callbacks.
* -
*/
diff --git a/services/core/java/com/android/server/task/TaskStore.java b/services/core/java/com/android/server/task/TaskStore.java
index 3bfc8a5..81187c8 100644
--- a/services/core/java/com/android/server/task/TaskStore.java
+++ b/services/core/java/com/android/server/task/TaskStore.java
@@ -16,8 +16,8 @@
package com.android.server.task;
+import android.app.task.Task;
import android.content.Context;
-import android.content.Task;
import android.util.SparseArray;
import com.android.server.task.controllers.TaskStatus;
@@ -95,6 +95,13 @@
}
/**
+ * @return the number of tasks in the store
+ */
+ public int size() {
+ return mTasks.size();
+ }
+
+ /**
* @return The live array of TaskStatus objects.
*/
public SparseArray<TaskStatus> getTasks() {
diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
index 6a4e1f3..a0038c5d 100644
--- a/services/core/java/com/android/server/task/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
@@ -70,13 +70,10 @@
}
/**
- * @param userId Id of the user for whom we are updating the connectivity state.
+ *
*/
- private void updateTrackedTasks(int userId) {
+ private void updateTrackedTasks() {
for (TaskStatus ts : mTrackedTasks) {
- if (ts.userId != userId) {
- continue;
- }
boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(mConnectivity);
boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(mMetered);
if (prevIsConnected != mConnectivity || prevIsMetered != mMetered) {
@@ -107,14 +104,13 @@
final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
// This broadcast gets sent a lot, only update if the active network has changed.
if (activeNetwork.getType() == networkType) {
- final int userid = context.getUserId();
mMetered = false;
mConnectivity =
!intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
if (mConnectivity) { // No point making the call if we know there's no conn.
mMetered = connManager.isActiveNetworkMetered();
}
- updateTrackedTasks(userid);
+ updateTrackedTasks();
}
} else {
Log.w(TAG, "Unrecognised action in intent: " + action);
diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java
index d96fedc..d270016 100644
--- a/services/core/java/com/android/server/task/controllers/TaskStatus.java
+++ b/services/core/java/com/android/server/task/controllers/TaskStatus.java
@@ -16,17 +16,19 @@
package com.android.server.task.controllers;
+import android.app.task.Task;
import android.content.ComponentName;
-import android.content.Task;
import android.content.pm.PackageParser;
import android.os.Bundle;
import android.os.SystemClock;
+import android.os.UserHandle;
+import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Uniquely identifies a task internally.
- * Created from the public {@link android.content.Task} object when it lands on the scheduler.
+ * Created from the public {@link android.app.task.Task} object when it lands on the scheduler.
* Contains current state of the requirements of the task, as well as a function to evaluate
* whether it's ready to run.
* This object is shared among the various controllers - hence why the different fields are atomic.
@@ -36,10 +38,9 @@
* @hide
*/
public class TaskStatus {
+ final Task task;
final int taskId;
- final int userId;
final int uId;
- final ComponentName component;
final Bundle extras;
final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
@@ -57,9 +58,9 @@
private long earliestRunTimeElapsedMillis;
private long latestRunTimeElapsedMillis;
- /** Provide a unique handle to the service that this task will be run on. */
+ /** Provide a handle to the service that this task will be run on. */
public int getServiceToken() {
- return component.hashCode() + userId;
+ return uId;
}
/** Generate a TaskStatus object for a given task and uid. */
@@ -70,9 +71,8 @@
/** Set up the state of a newly scheduled task. */
TaskStatus(Task task, int userId, int uId) {
+ this.task = task;
this.taskId = task.getTaskId();
- this.userId = userId;
- this.component = task.getService();
this.extras = task.getExtras();
this.uId = uId;
@@ -100,16 +100,20 @@
hasConnectivityConstraint = task.getNetworkCapabilities() == Task.NetworkType.ANY;
}
+ public Task getTask() {
+ return task;
+ }
+
public int getTaskId() {
return taskId;
}
public ComponentName getServiceComponent() {
- return component;
+ return task.getService();
}
public int getUserId() {
- return userId;
+ return UserHandle.getUserId(uId);
}
public int getUid() {
@@ -161,9 +165,9 @@
@Override
public int hashCode() {
- int result = component.hashCode();
+ int result = getServiceComponent().hashCode();
result = 31 * result + taskId;
- result = 31 * result + userId;
+ result = 31 * result + uId;
return result;
}
@@ -174,7 +178,14 @@
TaskStatus that = (TaskStatus) o;
return ((taskId == that.taskId)
- && (userId == that.userId)
- && (component.equals(that.component)));
+ && (uId == that.uId)
+ && (getServiceComponent().equals(that.getServiceComponent())));
+ }
+
+ // Dumpsys infrastructure
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("Task "); pw.println(taskId);
+ pw.print(prefix); pw.print("uid="); pw.println(uId);
+ pw.print(prefix); pw.print("component="); pw.println(task.getService());
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
new file mode 100644
index 0000000..4bdd2be
--- /dev/null
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -0,0 +1,128 @@
+/*
+ * 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.server.tv;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.tv.TvInputHardwareInfo;
+import android.tv.TvStreamConfig;
+import android.view.Surface;
+
+/**
+ * Provides access to the low-level TV input hardware abstraction layer.
+ */
+final class TvInputHal {
+ public final static int SUCCESS = 0;
+ public final static int ERROR_NO_INIT = -1;
+ public final static int ERROR_STALE_CONFIG = -2;
+ public final static int ERROR_UNKNOWN = -3;
+
+ public static final int TYPE_HDMI = 1;
+ public static final int TYPE_BUILT_IN_TUNER = 2;
+ public static final int TYPE_PASSTHROUGH = 3;
+
+ public interface Callback {
+ public void onDeviceAvailable(
+ TvInputHardwareInfo info, TvStreamConfig[] configs);
+ public void onDeviceUnavailable(int deviceId);
+ public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs);
+ }
+
+ private native long nativeOpen();
+
+ private static native int nativeSetSurface(long ptr, int deviceId, int streamId,
+ Surface surface);
+ private static native TvStreamConfig[] nativeGetStreamConfigs(long ptr, int deviceId,
+ int generation);
+ private static native void nativeClose(long ptr);
+
+ private long mPtr = 0l;
+ private final Callback mCallback;
+ private final HandlerThread mThread = new HandlerThread("TV input HAL event thread");
+ private final Handler mHandler;
+ private int mStreamConfigGeneration = 0;
+ private TvStreamConfig[] mStreamConfigs;
+
+ public TvInputHal(Callback callback) {
+ mCallback = callback;
+ mThread.start();
+ mHandler = new Handler(mThread.getLooper());
+ }
+
+ public void init() {
+ mPtr = nativeOpen();
+ }
+
+ public int setSurface(int deviceId, Surface surface, TvStreamConfig streamConfig) {
+ if (mPtr == 0) {
+ return ERROR_NO_INIT;
+ }
+ if (mStreamConfigGeneration != streamConfig.getGeneration()) {
+ return ERROR_STALE_CONFIG;
+ }
+ if (nativeSetSurface(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) {
+ return SUCCESS;
+ } else {
+ return ERROR_UNKNOWN;
+ }
+ }
+
+ public void close() {
+ if (mPtr != 0l) {
+ nativeClose(mPtr);
+ mThread.quitSafely();
+ }
+ }
+
+ private synchronized void retrieveStreamConfigs(int deviceId) {
+ ++mStreamConfigGeneration;
+ mStreamConfigs = nativeGetStreamConfigs(mPtr, deviceId, mStreamConfigGeneration);
+ }
+
+ // Called from native
+ private void deviceAvailableFromNative(int deviceId, int type) {
+ final TvInputHardwareInfo info = new TvInputHardwareInfo(deviceId, type);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ retrieveStreamConfigs(info.getDeviceId());
+ mCallback.onDeviceAvailable(info, mStreamConfigs);
+ }
+ });
+ }
+
+ private void deviceUnavailableFromNative(int deviceId) {
+ final int id = deviceId;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onDeviceUnavailable(id);
+ }
+ });
+ }
+
+ private void streamConfigsChangedFromNative(int deviceId) {
+ final int id = deviceId;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ retrieveStreamConfigs(id);
+ mCallback.onStreamConfigurationChanged(id, mStreamConfigs);
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
new file mode 100644
index 0000000..b95b0f0
--- /dev/null
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -0,0 +1,308 @@
+/*
+ * 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.tv;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.tv.ITvInputHardware;
+import android.tv.ITvInputHardwareCallback;
+import android.tv.TvInputHardwareInfo;
+import android.tv.TvStreamConfig;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.KeyEvent;
+import android.view.Surface;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A helper class for TvInputManagerService to handle TV input hardware.
+ *
+ * This class does a basic connection management and forwarding calls to TvInputHal which eventually
+ * calls to tv_input HAL module.
+ *
+ * @hide
+ */
+class TvInputHardwareManager implements TvInputHal.Callback {
+ private static final String TAG = TvInputHardwareManager.class.getSimpleName();
+ 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 Set<Integer> mActiveHdmiSources = new HashSet<Integer>();
+
+ private final Object mLock = new Object();
+
+ public TvInputHardwareManager(Context context) {
+ mContext = context;
+ // TODO(hdmi): mHdmiManager = mContext.getSystemService(...);
+ // TODO(hdmi): mHdmiClient = mHdmiManager.getTvClient();
+ mHal.init();
+ }
+
+ @Override
+ public void onDeviceAvailable(
+ TvInputHardwareInfo info, TvStreamConfig[] configs) {
+ synchronized (mLock) {
+ Connection connection = new Connection(info);
+ connection.updateConfigsLocked(configs);
+ mConnections.put(info.getDeviceId(), connection);
+ buildInfoListLocked();
+ // TODO: notify if necessary
+ }
+ }
+
+ private void buildInfoListLocked() {
+ mInfoList.clear();
+ for (int i = 0; i < mConnections.size(); ++i) {
+ mInfoList.add(mConnections.valueAt(i).getInfoLocked());
+ }
+ }
+
+ @Override
+ public void onDeviceUnavailable(int deviceId) {
+ synchronized (mLock) {
+ Connection connection = mConnections.get(deviceId);
+ if (connection == null) {
+ Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
+ return;
+ }
+ connection.resetLocked(null, null, null, null);
+ mConnections.remove(deviceId);
+ buildInfoListLocked();
+ // TODO: notify if necessary
+ }
+ }
+
+ @Override
+ public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
+ synchronized (mLock) {
+ Connection connection = mConnections.get(deviceId);
+ if (connection == null) {
+ Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
+ + deviceId);
+ return;
+ }
+ connection.updateConfigsLocked(configs);
+ try {
+ connection.getCallbackLocked().onStreamConfigChanged(configs);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "onStreamConfigurationChanged: " + e);
+ }
+ }
+ }
+
+ public List<TvInputHardwareInfo> getHardwareList() {
+ synchronized (mLock) {
+ return mInfoList;
+ }
+ }
+
+ /**
+ * 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,
+ * the latest service will get the object and all the other hardware are released. The
+ * release is notified via ITvInputHardwareCallback.onReleased().
+ */
+ public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
+ int callingUid, int resolvedUserId) {
+ if (callback == null) {
+ throw new NullPointerException();
+ }
+ synchronized (mLock) {
+ Connection connection = mConnections.get(deviceId);
+ if (connection == null) {
+ Slog.e(TAG, "Invalid deviceId : " + deviceId);
+ return null;
+ }
+ if (connection.getCallingUidLocked() != callingUid
+ || connection.getResolvedUserIdLocked() != resolvedUserId) {
+ TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getInfoLocked());
+ try {
+ callback.asBinder().linkToDeath(connection, 0);
+ } catch (RemoteException e) {
+ hardware.release();
+ return null;
+ }
+ connection.resetLocked(hardware, callback, callingUid, resolvedUserId);
+ }
+ return connection.getHardwareLocked();
+ }
+ }
+
+ /**
+ * Release the specified hardware.
+ */
+ public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
+ int resolvedUserId) {
+ synchronized (mLock) {
+ Connection connection = mConnections.get(deviceId);
+ if (connection == null) {
+ Slog.e(TAG, "Invalid deviceId : " + deviceId);
+ return;
+ }
+ if (connection.getHardwareLocked() != hardware
+ || connection.getCallingUidLocked() != callingUid
+ || connection.getResolvedUserIdLocked() != resolvedUserId) {
+ return;
+ }
+ connection.resetLocked(null, null, null, null);
+ }
+ }
+
+ private class Connection implements IBinder.DeathRecipient {
+ private final TvInputHardwareInfo mInfo;
+ private TvInputHardwareImpl mHardware = null;
+ private ITvInputHardwareCallback mCallback;
+ private TvStreamConfig[] mConfigs = null;
+ private Integer mCallingUid = null;
+ private Integer mResolvedUserId = null;
+
+ public Connection(TvInputHardwareInfo info) {
+ mInfo = info;
+ }
+
+ // *Locked methods assume TvInputHardwareManager.mLock is held.
+
+ public void resetLocked(TvInputHardwareImpl hardware,
+ ITvInputHardwareCallback callback, Integer callingUid, Integer resolvedUserId) {
+ if (mHardware != null) {
+ try {
+ mCallback.onReleased();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Connection::resetHardware: " + e);
+ }
+ mHardware.release();
+ }
+ mHardware = hardware;
+ mCallback = callback;
+ mCallingUid = callingUid;
+ mResolvedUserId = resolvedUserId;
+
+ if (mHardware != null && mCallback != null) {
+ try {
+ mCallback.onStreamConfigChanged(getConfigsLocked());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Connection::resetHardware: " + e);
+ }
+ }
+ }
+
+ public void updateConfigsLocked(TvStreamConfig[] configs) {
+ mConfigs = configs;
+ }
+
+ public TvInputHardwareInfo getInfoLocked() {
+ return mInfo;
+ }
+
+ public ITvInputHardware getHardwareLocked() {
+ return mHardware;
+ }
+
+ public ITvInputHardwareCallback getCallbackLocked() {
+ return mCallback;
+ }
+
+ public TvStreamConfig[] getConfigsLocked() {
+ return mConfigs;
+ }
+
+ public int getCallingUidLocked() {
+ return mCallingUid;
+ }
+
+ public int getResolvedUserIdLocked() {
+ return mResolvedUserId;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ resetLocked(null, null, null, null);
+ }
+ }
+ }
+
+ private class TvInputHardwareImpl extends ITvInputHardware.Stub {
+ private final TvInputHardwareInfo mInfo;
+ private boolean mReleased = false;
+ private final Object mImplLock = new Object();
+
+ public TvInputHardwareImpl(TvInputHardwareInfo info) {
+ mInfo = info;
+ }
+
+ public void release() {
+ synchronized (mImplLock) {
+ mReleased = true;
+ }
+ }
+
+ @Override
+ public boolean setSurface(Surface surface, TvStreamConfig config)
+ throws RemoteException {
+ synchronized (mImplLock) {
+ if (mReleased) {
+ throw new IllegalStateException("Device already released.");
+ }
+ if (mInfo.getType() == TvInputHal.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);
+ }
+ }
+ }
+ return mHal.setSurface(mInfo.getDeviceId(), surface, config) == TvInputHal.SUCCESS;
+ }
+ }
+
+ @Override
+ public void setVolume(float volume) throws RemoteException {
+ synchronized (mImplLock) {
+ if (mReleased) {
+ throw new IllegalStateException("Device already released.");
+ }
+ }
+ // TODO
+ }
+
+ @Override
+ public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
+ synchronized (mImplLock) {
+ if (mReleased) {
+ throw new IllegalStateException("Device already released.");
+ }
+ }
+ if (mInfo.getType() != TvInputHal.TYPE_HDMI) {
+ return false;
+ }
+ // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
+ return false;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 8ad7fff..6c38a4c 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -42,11 +42,14 @@
import android.os.UserHandle;
import android.provider.TvContract;
import android.tv.ITvInputClient;
+import android.tv.ITvInputHardware;
+import android.tv.ITvInputHardwareCallback;
import android.tv.ITvInputManager;
import android.tv.ITvInputService;
import android.tv.ITvInputServiceCallback;
import android.tv.ITvInputSession;
import android.tv.ITvInputSessionCallback;
+import android.tv.TvInputHardwareInfo;
import android.tv.TvInputInfo;
import android.tv.TvInputService;
import android.util.Slog;
@@ -71,6 +74,7 @@
private static final String TAG = "TvInputManagerService";
private final Context mContext;
+ private final TvInputHardwareManager mTvInputHardwareManager;
private final ContentResolver mContentResolver;
@@ -92,6 +96,7 @@
mContentResolver = context.getContentResolver();
mLogHandler = new LogHandler(IoThread.get().getLooper());
+ mTvInputHardwareManager = new TvInputHardwareManager(context);
registerBroadcastReceivers();
synchronized (mLock) {
@@ -730,6 +735,64 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
+ if (mContext.checkCallingPermission(
+ android.Manifest.permission.TV_INPUT_HARDWARE)
+ != PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mTvInputHardwareManager.getHardwareList();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public ITvInputHardware acquireTvInputHardware(int deviceId,
+ ITvInputHardwareCallback callback, int userId) throws RemoteException {
+ if (mContext.checkCallingPermission(
+ android.Manifest.permission.TV_INPUT_HARDWARE)
+ != PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "acquireTvInputHardware");
+ try {
+ return mTvInputHardwareManager.acquireHardware(
+ deviceId, callback, callingUid, resolvedUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
+ throws RemoteException {
+ if (mContext.checkCallingPermission(
+ android.Manifest.permission.TV_INPUT_HARDWARE)
+ != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "releaseTvInputHardware");
+ try {
+ mTvInputHardwareManager.releaseHardware(
+ deviceId, hardware, callingUid, resolvedUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
private static final class UserState {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1a0dd82..c23d1ea 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3189,6 +3189,7 @@
WindowState win = atoken.findMainWindow();
Rect containingFrame = new Rect(0, 0, width, height);
Rect contentInsets = new Rect();
+ boolean isFullScreen = true;
if (win != null) {
if (win.mContainingFrame != null) {
containingFrame.set(win.mContainingFrame);
@@ -3196,11 +3197,11 @@
if (win.mContentInsets != null) {
contentInsets.set(win.mContentInsets);
}
+ isFullScreen =
+ ((win.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN) ==
+ SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN);
}
- boolean isFullScreen =
- ((win.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN)
- == SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN);
Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
mCurConfiguration.orientation, containingFrame, contentInsets, isFullScreen);
if (a != null) {
@@ -10265,6 +10266,10 @@
mPolicy.lockNow(options);
}
+ public void showRecentApps() {
+ mPolicy.showRecentApps();
+ }
+
@Override
public boolean isSafeModeEnabled() {
return mSafeMode;
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 51583a5..3cfb45b 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -23,6 +23,7 @@
$(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \
$(LOCAL_REL_DIR)/com_android_server_SystemServer.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_tv_TvInputHal.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
new file mode 100644
index 0000000..f0c4f3a
--- /dev/null
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -0,0 +1,388 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "TvInputHal"
+
+//#define LOG_NDEBUG 0
+
+#include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/android_view_Surface.h"
+#include "JNIHelp.h"
+#include "jni.h"
+
+#include <gui/Surface.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/NativeHandle.h>
+#include <hardware/tv_input.h>
+
+namespace android {
+
+static struct {
+ jmethodID deviceAvailable;
+ jmethodID deviceUnavailable;
+ jmethodID streamConfigsChanged;
+} gTvInputHalClassInfo;
+
+static struct {
+ jclass clazz;
+} gTvStreamConfigClassInfo;
+
+static struct {
+ jclass clazz;
+
+ jmethodID constructor;
+ jmethodID streamId;
+ jmethodID type;
+ jmethodID maxWidth;
+ jmethodID maxHeight;
+ jmethodID generation;
+ jmethodID build;
+} gTvStreamConfigBuilderClassInfo;
+
+////////////////////////////////////////////////////////////////////////////////
+
+class JTvInputHal {
+public:
+ ~JTvInputHal();
+
+ static JTvInputHal* createInstance(JNIEnv* env, jobject thiz);
+
+ int setSurface(int deviceId, int streamId, const sp<Surface>& surface);
+ void getStreamConfigs(int deviceId, jobjectArray* array);
+ const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
+
+private:
+ class Connection {
+ public:
+ Connection() : mStreamId(0) {}
+
+ sp<Surface> mSurface;
+ sp<NativeHandle> mSourceHandle;
+ int mStreamId;
+ };
+
+ JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev);
+
+ static void notify(
+ tv_input_device_t* dev,tv_input_event_t* event, void* data);
+
+ void onDeviceAvailable(const tv_input_device_info_t& info);
+ void onDeviceUnavailable(int deviceId);
+ void onStreamConfigurationsChanged(int deviceId);
+
+ jweak mThiz;
+ tv_input_device_t* mDevice;
+ tv_input_callback_ops_t mCallback;
+
+ KeyedVector<int, Connection> mConnections;
+};
+
+JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device) {
+ mThiz = env->NewWeakGlobalRef(thiz);
+ mDevice = device;
+ mCallback.notify = &JTvInputHal::notify;
+
+ mDevice->initialize(mDevice, &mCallback, this);
+}
+
+JTvInputHal::~JTvInputHal() {
+ mDevice->common.close((hw_device_t*)mDevice);
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->DeleteWeakGlobalRef(mThiz);
+ mThiz = NULL;
+}
+
+JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz) {
+ tv_input_module_t* module = NULL;
+ status_t err = hw_get_module(TV_INPUT_HARDWARE_MODULE_ID,
+ (hw_module_t const**)&module);
+ if (err) {
+ ALOGE("Couldn't load %s module (%s)",
+ TV_INPUT_HARDWARE_MODULE_ID, strerror(-err));
+ return 0;
+ }
+
+ tv_input_device_t* device = NULL;
+ err = module->common.methods->open(
+ (hw_module_t*)module,
+ TV_INPUT_DEFAULT_DEVICE,
+ (hw_device_t**)&device);
+ if (err) {
+ ALOGE("Couldn't open %s device (%s)",
+ TV_INPUT_DEFAULT_DEVICE, strerror(-err));
+ return 0;
+ }
+
+ return new JTvInputHal(env, thiz, device);
+}
+
+int JTvInputHal::setSurface(int deviceId, int streamId, const sp<Surface>& surface) {
+ Connection& connection = mConnections.editValueFor(deviceId);
+ if (connection.mStreamId == streamId && connection.mSurface == surface) {
+ // Nothing to do
+ return NO_ERROR;
+ }
+ if (Surface::isValid(connection.mSurface)) {
+ connection.mSurface.clear();
+ }
+ if (surface == NULL) {
+ if (connection.mSurface != NULL) {
+ connection.mSurface->setSidebandStream(NULL);
+ connection.mSurface.clear();
+ }
+ if (connection.mSourceHandle != NULL) {
+ // Need to reset streams
+ if (mDevice->close_stream(
+ mDevice, deviceId, connection.mStreamId) != 0) {
+ ALOGE("Couldn't remove stream");
+ return BAD_VALUE;
+ }
+ connection.mSourceHandle.clear();
+ }
+ return NO_ERROR;
+ }
+ connection.mSurface = surface;
+ if (connection.mSourceHandle == NULL) {
+ // Need to configure stream
+ int numConfigs = 0;
+ const tv_stream_config_t* configs = NULL;
+ if (mDevice->get_stream_configurations(
+ mDevice, deviceId, &numConfigs, &configs) != 0) {
+ ALOGE("Couldn't get stream configs");
+ return UNKNOWN_ERROR;
+ }
+ int configIndex = -1;
+ for (int i = 0; i < numConfigs; ++i) {
+ if (configs[i].stream_id == streamId) {
+ configIndex = i;
+ break;
+ }
+ }
+ if (configIndex == -1) {
+ ALOGE("Cannot find a config with given stream ID: %d", streamId);
+ return BAD_VALUE;
+ }
+ // TODO: handle buffer producer profile.
+ if (configs[configIndex].type !=
+ TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+ ALOGE("Profiles other than independent video source is not yet "
+ "supported : type = %d", configs[configIndex].type);
+ return INVALID_OPERATION;
+ }
+ tv_stream_t stream;
+ stream.stream_id = configs[configIndex].stream_id;
+ if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
+ ALOGE("Couldn't add stream");
+ return UNKNOWN_ERROR;
+ }
+ connection.mSourceHandle = NativeHandle::create(
+ stream.sideband_stream_source_handle, false);
+ connection.mStreamId = stream.stream_id;
+ connection.mSurface->setSidebandStream(connection.mSourceHandle);
+ }
+ return NO_ERROR;
+}
+
+const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) {
+ const tv_stream_config_t* configs = NULL;
+ if (mDevice->get_stream_configurations(
+ mDevice, deviceId, numConfigs, &configs) != 0) {
+ ALOGE("Couldn't get stream configs");
+ return NULL;
+ }
+ return configs;
+}
+
+
+// static
+void JTvInputHal::notify(
+ tv_input_device_t* dev, tv_input_event_t* event, void* data) {
+ JTvInputHal* thiz = (JTvInputHal*)data;
+ switch (event->type) {
+ case TV_INPUT_EVENT_DEVICE_AVAILABLE: {
+ thiz->onDeviceAvailable(event->device_info);
+ } break;
+ case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: {
+ thiz->onDeviceUnavailable(event->device_info.device_id);
+ } break;
+ case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
+ thiz->onStreamConfigurationsChanged(event->device_info.device_id);
+ } break;
+ default:
+ ALOGE("Unrecognizable event");
+ }
+}
+
+void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ mConnections.add(info.device_id, Connection());
+ env->CallVoidMethod(
+ mThiz,
+ gTvInputHalClassInfo.deviceAvailable,
+ info.device_id,
+ info.type);
+}
+
+void JTvInputHal::onDeviceUnavailable(int deviceId) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ mConnections.removeItem(deviceId);
+ env->CallVoidMethod(
+ mThiz,
+ gTvInputHalClassInfo.deviceUnavailable,
+ deviceId);
+}
+
+void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ mConnections.removeItem(deviceId);
+ env->CallVoidMethod(
+ mThiz,
+ gTvInputHalClassInfo.streamConfigsChanged,
+ deviceId);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static jlong nativeOpen(JNIEnv* env, jobject thiz) {
+ return (jlong)JTvInputHal::createInstance(env, thiz);
+}
+
+static int nativeSetSurface(JNIEnv* env, jclass clazz,
+ jlong ptr, jint deviceId, jint streamId, jobject jsurface) {
+ JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
+ sp<Surface> surface(
+ jsurface
+ ? android_view_Surface_getSurface(env, jsurface)
+ : NULL);
+ return tvInputHal->setSurface(deviceId, streamId, surface);
+}
+
+static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz,
+ jlong ptr, jint deviceId, jint generation) {
+ JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
+ int numConfigs = 0;
+ const tv_stream_config_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs);
+
+ jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL);
+ for (int i = 0; i < numConfigs; ++i) {
+ jobject builder = env->NewObject(
+ gTvStreamConfigBuilderClassInfo.clazz,
+ gTvStreamConfigBuilderClassInfo.constructor);
+ env->CallObjectMethod(
+ builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id);
+ env->CallObjectMethod(
+ builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type);
+ env->CallObjectMethod(
+ builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width);
+ env->CallObjectMethod(
+ builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height);
+ env->CallObjectMethod(
+ builder, gTvStreamConfigBuilderClassInfo.generation, generation);
+
+ jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build);
+
+ env->SetObjectArrayElement(result, i, config);
+
+ env->DeleteLocalRef(config);
+ env->DeleteLocalRef(builder);
+ }
+ return result;
+}
+
+static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
+ JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
+ delete tvInputHal;
+}
+
+static JNINativeMethod gTvInputHalMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeOpen", "()J",
+ (void*) nativeOpen },
+ { "nativeSetSurface", "(JIILandroid/view/Surface;)I",
+ (void*) nativeSetSurface },
+ { "nativeGetStreamConfigs", "(JII)[Landroid/tv/TvStreamConfig;",
+ (void*) nativeGetStreamConfigs },
+ { "nativeClose", "(J)V",
+ (void*) nativeClose },
+};
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className)
+
+#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
+ var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method" methodName)
+
+int register_android_server_tv_TvInputHal(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "com/android/server/tv/TvInputHal",
+ gTvInputHalMethods, NELEM(gTvInputHalMethods));
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ jclass clazz;
+ FIND_CLASS(clazz, "com/android/server/tv/TvInputHal");
+
+ GET_METHOD_ID(
+ gTvInputHalClassInfo.deviceAvailable, clazz, "deviceAvailableFromNative", "(II)V");
+ GET_METHOD_ID(
+ gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V");
+ GET_METHOD_ID(
+ gTvInputHalClassInfo.streamConfigsChanged, clazz,
+ "streamConfigsChangedFromNative", "(I)V");
+
+ FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/tv/TvStreamConfig");
+ gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
+
+ FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/tv/TvStreamConfig$Builder");
+ gTvStreamConfigBuilderClassInfo.clazz =
+ jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz));
+
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.constructor,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "<init>", "()V");
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.streamId,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "streamId", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.type,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "type", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.maxWidth,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "maxWidth", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.maxHeight,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "maxHeight", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.generation,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "generation", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.build,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "build", "()Landroid/tv/TvStreamConfig;");
+
+ return 0;
+}
+
+} /* namespace android */
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 1feb325..bfa8286 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -41,6 +41,7 @@
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
int register_android_server_hdmi_HdmiCecService(JNIEnv* env);
int register_android_server_hdmi_HdmiMhlController(JNIEnv* env);
+int register_android_server_tv_TvInputHal(JNIEnv* env);
};
using namespace android;
@@ -78,6 +79,7 @@
// TODO: remove this once replaces HdmiCecService with HdmiControlService.
register_android_server_hdmi_HdmiCecService(env);
register_android_server_hdmi_HdmiMhlController(env);
+ register_android_server_tv_TvInputHal(env);
return JNI_VERSION_1_4;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4e4d127..d78fb13 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3303,7 +3303,7 @@
}
}
- public void addForwardingIntentFilter(ComponentName who, IntentFilter filter, int flags) {
+ public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) {
int callingUserId = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
@@ -3314,12 +3314,12 @@
IPackageManager pm = AppGlobals.getPackageManager();
long id = Binder.clearCallingIdentity();
try {
- if ((flags & DevicePolicyManager.FLAG_TO_PRIMARY_USER) != 0) {
- pm.addForwardingIntentFilter(filter, true /*removable*/, callingUserId,
+ if ((flags & DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED) != 0) {
+ pm.addCrossProfileIntentFilter(filter, true /*removable*/, callingUserId,
UserHandle.USER_OWNER);
}
- if ((flags & DevicePolicyManager.FLAG_TO_MANAGED_PROFILE) != 0) {
- pm.addForwardingIntentFilter(filter, true /*removable*/, UserHandle.USER_OWNER,
+ if ((flags & DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT) != 0) {
+ pm.addCrossProfileIntentFilter(filter, true /*removable*/, UserHandle.USER_OWNER,
callingUserId);
}
} catch (RemoteException re) {
@@ -3330,7 +3330,7 @@
}
}
- public void clearForwardingIntentFilters(ComponentName who) {
+ public void clearCrossProfileIntentFilters(ComponentName who) {
int callingUserId = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
@@ -3340,8 +3340,8 @@
IPackageManager pm = AppGlobals.getPackageManager();
long id = Binder.clearCallingIdentity();
try {
- pm.clearForwardingIntentFilters(callingUserId);
- pm.clearForwardingIntentFilters(UserHandle.USER_OWNER);
+ pm.clearCrossProfileIntentFilters(callingUserId);
+ pm.clearCrossProfileIntentFilters(UserHandle.USER_OWNER);
} catch (RemoteException re) {
// Shouldn't happen
} finally {
@@ -3351,6 +3351,44 @@
}
@Override
+ public UserHandle createUser(ComponentName who, String name) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+ long id = Binder.clearCallingIdentity();
+ try {
+ UserInfo userInfo = mUserManager.createUser(name, 0 /* flags */);
+ if (userInfo != null) {
+ return userInfo.getUserHandle();
+ }
+ return null;
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ }
+ }
+
+ @Override
+ public boolean removeUser(ComponentName who, UserHandle userHandle) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+ long id = Binder.clearCallingIdentity();
+ try {
+ return mUserManager.removeUser(userHandle.getIdentifier());
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ }
+ }
+
+ @Override
public Bundle getApplicationRestrictions(ComponentName who, String packageName) {
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index de46b16..bc34e0ef 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -83,6 +83,7 @@
import com.android.server.search.SearchManagerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
+import com.android.server.task.TaskManagerService;
import com.android.server.trust.TrustManagerService;
import com.android.server.tv.TvInputManagerService;
import com.android.server.twilight.TwilightService;
@@ -132,6 +133,8 @@
"com.android.server.hdmi.HdmiCecService";
private static final String ETHERNET_SERVICE_CLASS =
"com.android.server.ethernet.EthernetService";
+ private static final String TASK_SERVICE_CLASS =
+ "com.android.server.task.TaskManagerService";
private final int mFactoryTestMode;
private Timer mProfilerSnapshotTimer;
@@ -831,6 +834,8 @@
mSystemServiceManager.startService(UiModeManagerService.class);
+ mSystemServiceManager.startService(TaskManagerService.class);
+
if (!disableNonCoreServices) {
try {
if (pm.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
new file mode 100644
index 0000000..c439211
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.internal.telecomm;
+
+/**
+ * Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing
+ * commands that were previously handled by ITelephony.
+ * {@hide}
+ */
+oneway interface ITelecommService {
+
+ /**
+ * Silence the ringer if an incoming call is currently ringing.
+ * (If vibrating, stop the vibrator also.)
+ *
+ * It's safe to call this if the ringer has already been silenced, or
+ * even if there's no incoming call. (If so, this method will do nothing.)
+ */
+ void silenceRinger();
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a896861..2483419 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -30,6 +30,7 @@
import android.telephony.Rlog;
import android.util.Log;
+import com.android.internal.telecomm.ITelecommService;
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -65,6 +66,8 @@
public class TelephonyManager {
private static final String TAG = "TelephonyManager";
+ private static final String TELECOMM_SERVICE_NAME = "telecomm";
+
private static ITelephonyRegistry sRegistry;
/**
@@ -1536,6 +1539,10 @@
return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
}
+ private ITelecommService getTelecommService() {
+ return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
+ }
+
//
//
// PhoneStateListener
@@ -1745,6 +1752,8 @@
*
* @param AID Application id. See ETSI 102.221 and 101.220.
* @return The logical channel id which is negative on error.
+ *
+ * @hide
*/
public int iccOpenLogicalChannel(String AID) {
try {
@@ -1766,6 +1775,8 @@
* @param channel is the channel id to be closed as retruned by a successful
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
+ *
+ * @hide
*/
public boolean iccCloseLogicalChannel(int channel) {
try {
@@ -1795,6 +1806,8 @@
* @param data Data to be sent with the APDU.
* @return The APDU response from the ICC card with the status appended at
* the end. If an error occurs, an empty string is returned.
+ *
+ * @hide
*/
public String iccTransmitApduLogicalChannel(int channel, int cla,
int instruction, int p1, int p2, int p3, String data) {
@@ -1819,6 +1832,8 @@
* @return The APDU response from the ICC card, with the last 4 bytes
* being the status word. If the command fails, returns an empty
* string.
+ *
+ * @hide
*/
public String sendEnvelopeWithStatus(String content) {
try {
@@ -2016,9 +2031,9 @@
@PrivateApi
public void silenceRinger() {
try {
- getITelephony().silenceRinger();
+ getTelecommService().silenceRinger();
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#silenceRinger", e);
+ Log.e(TAG, "Error calling ITelecommService#silenceRinger", e);
}
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 3190fb0..17db1b4 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -707,8 +707,8 @@
* @hide
*/
@Override
- public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int userIdOrig,
- int userIdDest) {
+ public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable,
+ int sourceUserId, int targetUserId) {
throw new UnsupportedOperationException();
}
@@ -716,7 +716,24 @@
* @hide
*/
@Override
- public void clearForwardingIntentFilters(int userIdOrig) {
+ public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int sourceUserId,
+ int targetUserId) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void clearCrossProfileIntentFilters(int sourceUserId) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void clearForwardingIntentFilters(int sourceUserId) {
throw new UnsupportedOperationException();
}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
index 6ad01a0..0f4e122 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
@@ -302,6 +302,36 @@
paint.setShader(ResourceModifiers.instance().mVertGradient);
}
});
+ put("radGradient", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setShader(ResourceModifiers.instance().mRadGradient);
+ }
+ });
+ put("sweepGradient", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setShader(ResourceModifiers.instance().mSweepGradient);
+ }
+ });
+ put("composeShader", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setShader(ResourceModifiers.instance().mComposeShader);
+ }
+ });
+ put("bad composeShader", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setShader(ResourceModifiers.instance().mBadComposeShader);
+ }
+ });
+ put("bad composeShader 2", new DisplayModifier() {
+ @Override
+ public void modifyDrawing(Paint paint, Canvas canvas) {
+ paint.setShader(ResourceModifiers.instance().mAnotherBadComposeShader);
+ }
+ });
}
});
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java
index c705443..d522481 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java
@@ -23,7 +23,11 @@
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Color;
+import android.graphics.ComposeShader;
import android.graphics.LinearGradient;
+import android.graphics.PorterDuff;
+import android.graphics.RadialGradient;
+import android.graphics.SweepGradient;
import android.graphics.Matrix;
import android.graphics.Shader;
@@ -38,6 +42,11 @@
public final LinearGradient mHorGradient;
public final LinearGradient mDiagGradient;
public final LinearGradient mVertGradient;
+ public final RadialGradient mRadGradient;
+ public final SweepGradient mSweepGradient;
+ public final ComposeShader mComposeShader;
+ public final ComposeShader mBadComposeShader;
+ public final ComposeShader mAnotherBadComposeShader;
public final Bitmap mBitmap;
private final Matrix mMtx1;
private final Matrix mMtx2;
@@ -90,6 +99,12 @@
mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
+ mSweepGradient = new SweepGradient(mDrawWidth / 2.0f, mDrawHeight / 2.0f,
+ Color.YELLOW, Color.MAGENTA);
+
+ mComposeShader = new ComposeShader(mRepeatShader, mHorGradient,
+ PorterDuff.Mode.MULTIPLY);
+
final float width = mBitmap.getWidth() / 8.0f;
final float height = mBitmap.getHeight() / 8.0f;
@@ -106,6 +121,16 @@
0xff00ff00, 0xff0000ff, 0xffff0000, 0xff00ff00,
0x00ff0000, 0x0000ff00, 0x000000ff, 0x00ff0000,
};
+
+ // Use a repeating gradient with many colors to test the non simple case.
+ mRadGradient = new RadialGradient(mDrawWidth / 4.0f, mDrawHeight / 4.0f, 4.0f,
+ mBitmapColors, null, Shader.TileMode.REPEAT);
+
+ mBadComposeShader = new ComposeShader(mRadGradient, mComposeShader,
+ PorterDuff.Mode.MULTIPLY);
+
+ mAnotherBadComposeShader = new ComposeShader(mRadGradient, mVertGradient,
+ PorterDuff.Mode.MULTIPLY);
}
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index bafc71e..1157de7d 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -509,8 +509,6 @@
* @hide
*/
public boolean isValid() {
- if (SSID == null)
- return false;
if (allowedKeyManagement == null)
return false;
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 1484d49..7debb93 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -224,9 +224,9 @@
public static final int TTLS = 2;
/** EAP-Password */
public static final int PWD = 3;
- /** EAP-Subscriber Identity Module */
+ /** EAP-Subscriber Identity Module {@hide} */
public static final int SIM = 4;
- /** EAP-Authentication and Key Agreement */
+ /** EAP-Authentication and Key Agreement {@hide} */
public static final int AKA = 5;
/** @hide */
public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA" };
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
index 08b430f..090ac56 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
@@ -25,13 +25,18 @@
import java.util.Iterator;
import java.util.Map;
+/**
+ * A class representing a Wi-Fi Passpoint credential.
+ */
public class WifiPasspointCredential implements Parcelable {
private final static String TAG = "PasspointCredential";
+ private String mWifiTreePath;
private String mWifiSPFQDN;
private String mCredentialName;
private String mUpdateIdentifier;
private String mSubscriptionUpdateMethod;
+ private WifiEnterpriseConfig mEnterpriseConfig;
private String mType;
private String mInnerMethod;
private String mCertType;
@@ -88,17 +93,16 @@
/**
* Constructor
* @param realm Realm of the passpoint credential
- * @param config Credential information, must be either EAP-TLS or EAP-TTLS.
+ * @param fqdn Fully qualified domain name (FQDN) of the credential
+ * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS
* @see WifiEnterpriseConfig
*/
- public WifiPasspointCredential(String realm, WifiEnterpriseConfig config) {
+ public WifiPasspointCredential(String realm, String fqdn, WifiEnterpriseConfig config) {
mRealm = realm;
switch (config.getEapMethod()) {
case WifiEnterpriseConfig.Eap.TLS:
- // TODO;
- break;
case WifiEnterpriseConfig.Eap.TTLS:
- // TODO;
+ mEnterpriseConfig = new WifiEnterpriseConfig(config);
break;
default:
// ignore
@@ -294,11 +298,21 @@
}
/**
- * Get EAP method of this Passpoint credential.
- * @return EAP method, refer to {@link WifiEnterpriseConfig.Eap} for possible return values
+ * Get enterprise config of this Passpoint credential.
+ * @return Enterprise config
+ * @see WifiEnterpriseConfig
*/
- public int getEapMethod() {
- return 0;
+ public WifiEnterpriseConfig getEnterpriseConfig() {
+ return new WifiEnterpriseConfig(mEnterpriseConfig);
+ }
+
+ /**
+ * Set enterprise config of this Passpoint credential.
+ * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS
+ * @see WifiEnterpriseConfig
+ */
+ public void setEnterpriseConfig(WifiEnterpriseConfig config) {
+ // TODO
}
/** @hide */
@@ -311,10 +325,7 @@
return mCertSha256Fingerprint;
}
- /**
- * Get the user name of this Passpoint credential, for EAP-TTLS only.
- * @return user name
- */
+ /** @hide */
public String getUserName() {
return mUsername;
}
@@ -325,10 +336,7 @@
return mPasswd;
}
- /**
- * Get the IMSI of this Passpoint credential, for EAP-SIM / EAP-AKA only.
- * @return IMSI
- */
+ /** @hide */
public String getImsi() {
return mImsi;
}
@@ -344,26 +352,31 @@
}
/** @hide */
- public String getCaRootCert() {
+ public String getCaRootCertPath() {
return mCaRootCert;
}
- /**
- * Get the client certificate path of this Passpoint credential, for EAP-TLS only.
- * @return client certificate path
- */
+ /** @hide */
public String getClientCertPath() {
return mClientCert;
}
/**
- * Get the realm of this Passpoint credential, for all EAP methods.
+ * Get the realm of this Passpoint credential.
* @return Realm
*/
public String getRealm() {
return mRealm;
}
+ /**
+ * Set the ream of this Passpoint credential.
+ * @param realm Realm
+ */
+ public void setRealm(String realm) {
+ mRealm = realm;
+ }
+
/** @hide */
public int getPriority() {
if (mUserPreferred) {
@@ -374,14 +387,22 @@
}
/**
- * Get the fully qualified domain name (FQDN) of this Passpoint credential,
- * for all EAP methods.
+ * Get the fully qualified domain name (FQDN) of this Passpoint credential.
* @return FQDN
*/
public String getFqdn() {
return mHomeSpFqdn;
}
+ /**
+ * Set the fully qualified domain name (FQDN) of this Passpoint credential.
+ * @param fqdn FQDN
+ */
+ public void setFqdn(String fqdn) {
+ mHomeSpFqdn = fqdn;
+ }
+
+
/** @hide */
public String getOtherhomepartners() {
return mOtherhomepartnerFqdn;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
index ee4dc5a..ebaa8c9 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
@@ -443,10 +443,9 @@
return null;
}
- /* TODO: add credential APIs */
-
/**
- * Give a list of all saved Passpoint credentials.
+ * Get a list of saved Passpoint credentials. Only those credentials owned
+ * by the caller will be returned.
*
* @return The list of credentials
*/